/**
 * @fileoverview Kernel is a class to provide type-checked accessing
 * (read/write bool/int32/string/...) on binary data.
 *
 * When creating the Kernel with the binary data, there is no deep
 * decoding done (which requires full type information). The deep decoding is
 * deferred until the first time accessing (when accessors can provide
 * full type information).
 *
 * Because accessors can be statically analyzed and stripped, unlike eager
 * binary decoding (which requires the full type information of all defined
 * fields), Kernel will only need the full type information of used
 * fields.
 */
goog.module('protobuf.runtime.Kernel');

const BinaryStorage = goog.require('protobuf.runtime.BinaryStorage');
const BufferDecoder = goog.require('protobuf.binary.BufferDecoder');
const ByteString = goog.require('protobuf.ByteString');
const Int64 = goog.require('protobuf.Int64');
const InternalMessage = goog.require('protobuf.binary.InternalMessage');
const Storage = goog.require('protobuf.runtime.Storage');
const WireType = goog.require('protobuf.binary.WireType');
const Writer = goog.require('protobuf.binary.Writer');
const reader = goog.require('protobuf.binary.reader');
const {
    CHECK_TYPE,
    checkCriticalElementIndex,
    checkCriticalState,
    checkCriticalType,
    checkCriticalTypeBool,
    checkCriticalTypeBoolArray,
    checkCriticalTypeByteString,
    checkCriticalTypeByteStringArray,
    checkCriticalTypeDouble,
    checkCriticalTypeDoubleArray,
    checkCriticalTypeFloat,
    checkCriticalTypeFloatIterable,
    checkCriticalTypeMessageArray,
    checkCriticalTypeSignedInt32,
    checkCriticalTypeSignedInt32Array,
    checkCriticalTypeSignedInt64,
    checkCriticalTypeSignedInt64Array,
    checkCriticalTypeString,
    checkCriticalTypeStringArray,
    checkCriticalTypeUnsignedInt32,
    checkCriticalTypeUnsignedInt32Array,
    checkDefAndNotNull,
    checkElementIndex,
    checkFieldNumber,
    checkFunctionExists,
    checkState,
    checkTypeDouble,
    checkTypeFloat,
    checkTypeSignedInt32,
    checkTypeSignedInt64,
    checkTypeUnsignedInt32
} = goog.require('protobuf.internal.checks');
const {Field, IndexEntry} = goog.require('protobuf.binary.field');
const {buildIndex} = goog.require('protobuf.binary.indexer');
const {createTag, get32BitVarintLength, getTagLength} = goog.require('protobuf.binary.tag');


/**
 * Validates the index entry has the correct wire type.
 * @param {!IndexEntry} indexEntry
 * @param {!WireType} expected
 */
function validateWireType(indexEntry, expected) {
    const wireType = Field.getWireType(indexEntry);
    checkCriticalState(
        wireType === expected,
        `Expected wire type: ${expected} but found: ${wireType}`);
}

/**
 * Checks if the object implements InternalMessage interface.
 * @param {?} obj
 * @return {!InternalMessage}
 */
function checkIsInternalMessage(obj) {
    const message = /** @type {!InternalMessage} */ (obj);
    checkFunctionExists(message.internalGetKernel);
    return message;
}

/**
 * Checks if the instanceCreator returns an instance that implements the
 * InternalMessage interface.
 * @param {function(!Kernel):T} instanceCreator
 * @template T
 */
function checkInstanceCreator(instanceCreator) {
    if (CHECK_TYPE) {
        const emptyMessage = instanceCreator(Kernel.createEmpty());
        checkFunctionExists(emptyMessage.internalGetKernel);
    }
}

/**
 * Reads the last entry of the index array using the given read function.
 * This is used to implement parsing singular primitive fields.
 * @param {!Array<!IndexEntry>} indexArray
 * @param {!BufferDecoder} bufferDecoder
 * @param {function(!BufferDecoder, number):T} readFunc
 * @param {!WireType} wireType
 * @return {T}
 * @template T
 */
function readOptional(indexArray, bufferDecoder, readFunc, wireType) {
    const index = indexArray.length - 1;
    checkElementIndex(index, indexArray.length);
    const indexEntry = indexArray[index];
    validateWireType(indexEntry, wireType);
    return readFunc(bufferDecoder, Field.getStartIndex(indexEntry));
}

/**
 * Converts all entries of the index array to the template type using given read
 * methods and return an Iterable containing those converted values.
 * Primitive repeated fields may be encoded either packed or unpacked. Thus, two
 * read methods are needed for those two cases.
 * This is used to implement parsing repeated primitive fields.
 * @param {!Array<!IndexEntry>} indexArray
 * @param {!BufferDecoder} bufferDecoder
 * @param {function(!BufferDecoder, number):T} singularReadFunc
 * @param {function(!BufferDecoder, number):!Array<T>} packedReadFunc
 * @param {!WireType} expectedWireType
 * @return {!Array<T>}
 * @template T
 */
function readRepeatedPrimitive(
    indexArray, bufferDecoder, singularReadFunc, packedReadFunc,
    expectedWireType) {
    // Fast path when there is a single packed entry.
    if (indexArray.length === 1 &&
        Field.getWireType(indexArray[0]) === WireType.DELIMITED) {
        return packedReadFunc(bufferDecoder, Field.getStartIndex(indexArray[0]));
    }

    let /** !Array<T> */ result = [];
    for (const indexEntry of indexArray) {
        const wireType = Field.getWireType(indexEntry);
        const startIndex = Field.getStartIndex(indexEntry);
        if (wireType === WireType.DELIMITED) {
            result = result.concat(packedReadFunc(bufferDecoder, startIndex));
        } else {
            validateWireType(indexEntry, expectedWireType);
            result.push(singularReadFunc(bufferDecoder, startIndex));
        }
    }
    return result;
}

/**
 * Converts all entries of the index array to the template type using the given
 * read function and return an Array containing those converted values. This is
 * used to implement parsing repeated non-primitive fields.
 * @param {!Array<!IndexEntry>} indexArray
 * @param {!BufferDecoder} bufferDecoder
 * @param {function(!BufferDecoder, number):T} singularReadFunc
 * @return {!Array<T>}
 * @template T
 */
function readRepeatedNonPrimitive(indexArray, bufferDecoder, singularReadFunc) {
    const result = new Array(indexArray.length);
    for (let i = 0; i < indexArray.length; i++) {
        validateWireType(indexArray[i], WireType.DELIMITED);
        result[i] =
            singularReadFunc(bufferDecoder, Field.getStartIndex(indexArray[i]));
    }
    return result;
}

/**
 * Converts all entries of the index array to the template type using the given
 * read function and return an Array containing those converted values. This is
 * used to implement parsing repeated non-primitive fields.
 * @param {!Array<!IndexEntry>} indexArray
 * @param {!BufferDecoder} bufferDecoder
 * @param {number} fieldNumber
 * @param {function(!Kernel):T} instanceCreator
 * @param {number=} pivot
 * @return {!Array<T>}
 * @template T
 */
function readRepeatedGroup(
    indexArray, bufferDecoder, fieldNumber, instanceCreator, pivot) {
    const result = new Array(indexArray.length);
    for (let i = 0; i < indexArray.length; i++) {
        result[i] = doReadGroup(
            bufferDecoder, indexArray[i], fieldNumber, instanceCreator, pivot);
    }
    return result;
}

/**
 * Creates a new bytes array to contain all data of a submessage.
 * When there are multiple entries, merge them together.
 * @param {!Array<!IndexEntry>} indexArray
 * @param {!BufferDecoder} bufferDecoder
 * @return {!BufferDecoder}
 */
function mergeMessageArrays(indexArray, bufferDecoder) {
    const dataArrays = indexArray.map(
        indexEntry =>
            reader.readDelimited(bufferDecoder, Field.getStartIndex(indexEntry)));
    return BufferDecoder.merge(dataArrays);
}

/**
 * @param {!Array<!IndexEntry>} indexArray
 * @param {!BufferDecoder} bufferDecoder
 * @param {number=} pivot
 * @return {!Kernel}
 */
function readAccessor(indexArray, bufferDecoder, pivot = undefined) {
    checkState(indexArray.length > 0);
    let accessorBuffer;
    // Faster access for one member.
    if (indexArray.length === 1) {
        const indexEntry = indexArray[0];
        validateWireType(indexEntry, WireType.DELIMITED);
        accessorBuffer =
            reader.readDelimited(bufferDecoder, Field.getStartIndex(indexEntry));
    } else {
        indexArray.forEach(indexEntry => {
            validateWireType(indexEntry, WireType.DELIMITED);
        });
        accessorBuffer = mergeMessageArrays(indexArray, bufferDecoder);
    }
    return Kernel.fromBufferDecoder_(accessorBuffer, pivot);
}

/**
 * Merges all index entries of the index array using the given read function.
 * This is used to implement parsing singular message fields.
 * @param {!Array<!IndexEntry>} indexArray
 * @param {!BufferDecoder} bufferDecoder
 * @param {function(!Kernel):T} instanceCreator
 * @param {number=} pivot
 * @return {T}
 * @template T
 */
function readMessage(indexArray, bufferDecoder, instanceCreator, pivot) {
    checkInstanceCreator(instanceCreator);
    const accessor = readAccessor(indexArray, bufferDecoder, pivot);
    return instanceCreator(accessor);
}

/**
 * Merges all index entries of the index array using the given read function.
 * This is used to implement parsing singular group fields.
 * @param {!Array<!IndexEntry>} indexArray
 * @param {!BufferDecoder} bufferDecoder
 * @param {number} fieldNumber
 * @param {function(!Kernel):T} instanceCreator
 * @param {number=} pivot
 * @return {T}
 * @template T
 */
function readGroup(
    indexArray, bufferDecoder, fieldNumber, instanceCreator, pivot) {
    checkInstanceCreator(instanceCreator);
    checkState(indexArray.length > 0);
    return doReadGroup(
        bufferDecoder, indexArray[indexArray.length - 1], fieldNumber,
        instanceCreator, pivot);
}

/**
 * Merges all index entries of the index array using the given read function.
 * This is used to implement parsing singular message fields.
 * @param {!BufferDecoder} bufferDecoder
 * @param {!IndexEntry} indexEntry
 * @param {number} fieldNumber
 * @param {function(!Kernel):T} instanceCreator
 * @param {number=} pivot
 * @return {T}
 * @template T
 */
function doReadGroup(
    bufferDecoder, indexEntry, fieldNumber, instanceCreator, pivot) {
    validateWireType(indexEntry, WireType.START_GROUP);
    const fieldStartIndex = Field.getStartIndex(indexEntry);
    const tag = createTag(WireType.START_GROUP, fieldNumber);
    const groupTagLength = get32BitVarintLength(tag);
    const groupLength = getTagLength(
        bufferDecoder, fieldStartIndex, WireType.START_GROUP, fieldNumber);
    const accessorBuffer = bufferDecoder.subBufferDecoder(
        fieldStartIndex, groupLength - groupTagLength);
    const kernel = Kernel.fromBufferDecoder_(accessorBuffer, pivot);
    return instanceCreator(kernel);
}

/**
 * @param {!Writer} writer
 * @param {number} fieldNumber
 * @param {?InternalMessage} value
 */
function writeMessage(writer, fieldNumber, value) {
    writer.writeDelimited(
        fieldNumber, checkDefAndNotNull(value).internalGetKernel().serialize());
}

/**
 * @param {!Writer} writer
 * @param {number} fieldNumber
 * @param {?InternalMessage} value
 */
function writeGroup(writer, fieldNumber, value) {
    const kernel = checkDefAndNotNull(value).internalGetKernel();
    writer.writeStartGroup(fieldNumber);
    kernel.serializeToWriter(writer);
    writer.writeEndGroup(fieldNumber);
}

/**
 * Writes the array of Messages into the writer for the given field number.
 * @param {!Writer} writer
 * @param {number} fieldNumber
 * @param {!Iterable<!InternalMessage>} values
 */
function writeRepeatedMessage(writer, fieldNumber, values) {
    for (const value of values) {
        writeMessage(writer, fieldNumber, value);
    }
}

/**
 * Writes the array of Messages into the writer for the given field number.
 * @param {!Writer} writer
 * @param {number} fieldNumber
 * @param {!Array<!InternalMessage>} values
 */
function writeRepeatedGroup(writer, fieldNumber, values) {
    for (const value of values) {
        writeGroup(writer, fieldNumber, value);
    }
}

/**
 * Array.from has a weird type definition in google3/javascript/externs/es6.js
 * and wants the mapping function accept strings.
 * @const {function((string|number)): number}
 */
const fround = /** @type {function((string|number)): number} */ (Math.fround);

/**
 * Wraps an array and exposes it as an Iterable. This class is used to provide
 * immutable access of the array to the caller.
 * @implements {Iterable<T>}
 * @template T
 */
class ArrayIterable {
    /**
     * @param {!Array<T>} array
     */
    constructor(array) {
        /** @private @const {!Array<T>} */
        this.array_ = array;
    }

    /** @return {!Iterator<T>} */
    [Symbol.iterator]() {
        return this.array_[Symbol.iterator]();
    }
}

/**
 * Accesses protobuf fields on binary format data. Binary data is decoded lazily
 * at the first access.
 * @final
 */
class Kernel {
    /**
     * Create a Kernel for the given binary bytes.
     * The bytes array is kept by the Kernel. DON'T MODIFY IT.
     * @param {!ArrayBuffer} arrayBuffer Binary bytes.
     * @param {number=} pivot Fields with a field number no greater than the pivot
     *     value will be stored in an array for fast access. Other fields will be
     *     stored in a map. A higher pivot value can improve runtime performance
     *     at the expense of requiring more memory. It's recommended to set the
     *     value to the max field number of the message unless the field numbers
     *     are too sparse. If the value is not set, a default value specified in
     *     storage.js will be used.
     * @return {!Kernel}
     */
    static fromArrayBuffer(arrayBuffer, pivot = undefined) {
        const bufferDecoder = BufferDecoder.fromArrayBuffer(arrayBuffer);
        return Kernel.fromBufferDecoder_(bufferDecoder, pivot);
    }

    /**
     * Creates an empty Kernel.
     * @param {number=} pivot Fields with a field number no greater than the pivot
     *     value will be stored in an array for fast access. Other fields will be
     *     stored in a map. A higher pivot value can improve runtime performance
     *     at the expense of requiring more memory. It's recommended to set the
     *     value to the max field number of the message unless the field numbers
     *     are too sparse. If the value is not set, a default value specified in
     *     storage.js will be used.
     * @return {!Kernel}
     */
    static createEmpty(pivot = undefined) {
        return new Kernel(/* bufferDecoder= */ null, new BinaryStorage(pivot));
    }

    /**
     * Create a Kernel for the given binary bytes.
     * The bytes array is kept by the Kernel. DON'T MODIFY IT.
     * @param {!BufferDecoder} bufferDecoder Binary bytes.
     * @param {number|undefined} pivot
     * @return {!Kernel}
     * @private
     */
    static fromBufferDecoder_(bufferDecoder, pivot) {
        return new Kernel(bufferDecoder, buildIndex(bufferDecoder, pivot));
    }

    /**
     * @param {?BufferDecoder} bufferDecoder Binary bytes. Accessor treats the
     *     bytes as immutable and will never attempt to write to it.
     * @param {!Storage<!Field>} fields A map of field number to Field. The
     *     IndexEntry in each Field needs to be populated with the location of the
     *     field in the binary data.
     * @private
     */
    constructor(bufferDecoder, fields) {
        /** @private @const {?BufferDecoder} */
        this.bufferDecoder_ = bufferDecoder;
        /** @private @const {!Storage<!Field>} */
        this.fields_ = fields;
    }

    /**
     * Creates a shallow copy of the accessor.
     * @return {!Kernel}
     */
    shallowCopy() {
        return new Kernel(this.bufferDecoder_, this.fields_.shallowCopy());
    }

    /**
     * See definition of the pivot parameter on the fromArrayBuffer() method.
     * @return {number}
     */
    getPivot() {
        return this.fields_.getPivot();
    }

    /**
     * Clears the field for the given field number.
     * @param {number} fieldNumber
     */
    clearField(fieldNumber) {
        this.fields_.delete(fieldNumber);
    }

    /**
     * Returns data for a field specified by the given field number. Also cache
     * the data if it doesn't already exist in the cache. When no data is
     * available, return the given default value.
     * @param {number} fieldNumber
     * @param {?T} defaultValue
     * @param {function(!Array<!IndexEntry>, !BufferDecoder):T} readFunc
     * @param {function(!Writer, number, T)=} encoder
     * @return {T}
     * @template T
     * @private
     */
    getFieldWithDefault_(
        fieldNumber, defaultValue, readFunc, encoder = undefined) {
        checkFieldNumber(fieldNumber);

        const field = this.fields_.get(fieldNumber);
        if (field === undefined) {
            return defaultValue;
        }

        if (field.hasDecodedValue()) {
            checkState(!encoder || !!field.getEncoder());
            return field.getDecodedValue();
        }

        const parsed = readFunc(
            checkDefAndNotNull(field.getIndexArray()),
            checkDefAndNotNull(this.bufferDecoder_));
        field.setCache(parsed, encoder);
        return parsed;
    }

    /**
     * Sets data for a singular field specified by the given field number.
     * @param {number} fieldNumber
     * @param {T} value
     * @param {function(!Writer, number, T)} encoder
     * @return {T}
     * @template T
     * @private
     */
    setField_(fieldNumber, value, encoder) {
        checkFieldNumber(fieldNumber);
        this.fields_.set(fieldNumber, Field.fromDecodedValue(value, encoder));
    }

    /**
     * Serializes internal contents to binary format bytes array to the
     * given writer.
     * @param {!Writer} writer
     * @package
     */
    serializeToWriter(writer) {
        // If we use for...of here, jscompiler returns an array of both types for
        // fieldNumber and field without specifying which type is for
        // field, which prevents us to use fieldNumber. Thus, we use
        // forEach here.
        this.fields_.forEach((field, fieldNumber) => {
            // If encoder doesn't exist, there is no need to encode the value
            // because the data in the index is still valid.
            if (field.getEncoder() !== undefined) {
                const encoder = checkDefAndNotNull(field.getEncoder());
                encoder(writer, fieldNumber, field.getDecodedValue());
                return;
            }

            const indexArr = field.getIndexArray();
            if (indexArr) {
                for (const indexEntry of indexArr) {
                    writer.writeTag(fieldNumber, Field.getWireType(indexEntry));
                    writer.writeBufferDecoder(
                        checkDefAndNotNull(this.bufferDecoder_),
                        Field.getStartIndex(indexEntry), Field.getWireType(indexEntry),
                        fieldNumber);
                }
            }
        });
    }

    /**
     * Serializes internal contents to binary format bytes array.
     * @return {!ArrayBuffer}
     */
    serialize() {
        const writer = new Writer();
        this.serializeToWriter(writer);
        return writer.getAndResetResultBuffer();
    }

    /**
     * Returns whether data exists at the given field number.
     * @param {number} fieldNumber
     * @return {boolean}
     */
    hasFieldNumber(fieldNumber) {
        checkFieldNumber(fieldNumber);
        const field = this.fields_.get(fieldNumber);

        if (field === undefined) {
            return false;
        }

        if (field.getIndexArray() !== null) {
            return true;
        }

        if (Array.isArray(field.getDecodedValue())) {
            // For repeated fields, existence is decided by number of elements.
            return (/** !Array<?> */ (field.getDecodedValue())).length > 0;
        }
        return true;
    }

    /***************************************************************************
     *                        OPTIONAL GETTER METHODS
     ***************************************************************************/

    /**
     * Returns data as boolean for the given field number.
     * If no default is given, use false as the default.
     * @param {number} fieldNumber
     * @param {boolean=} defaultValue
     * @return {boolean}
     */
    getBoolWithDefault(fieldNumber, defaultValue = false) {
        return this.getFieldWithDefault_(
            fieldNumber, defaultValue,
            (indexArray, bytes) =>
                readOptional(indexArray, bytes, reader.readBool, WireType.VARINT));
    }

    /**
     * Returns data as a ByteString for the given field number.
     * If no default is given, use false as the default.
     * @param {number} fieldNumber
     * @param {!ByteString=} defaultValue
     * @return {!ByteString}
     */
    getBytesWithDefault(fieldNumber, defaultValue = ByteString.EMPTY) {
        return this.getFieldWithDefault_(
            fieldNumber, defaultValue,
            (indexArray, bytes) => readOptional(
                indexArray, bytes, reader.readBytes, WireType.DELIMITED));
    }

    /**
     * Returns a double for the given field number.
     * If no default is given uses zero as the default.
     * @param {number} fieldNumber
     * @param {number=} defaultValue
     * @return {number}
     */
    getDoubleWithDefault(fieldNumber, defaultValue = 0) {
        checkTypeDouble(defaultValue);
        return this.getFieldWithDefault_(
            fieldNumber, defaultValue,
            (indexArray, bytes) => readOptional(
                indexArray, bytes, reader.readDouble, WireType.FIXED64));
    }

    /**
     * Returns a fixed32 for the given field number.
     * If no default is given zero as the default.
     * @param {number} fieldNumber
     * @param {number=} defaultValue
     * @return {number}
     */
    getFixed32WithDefault(fieldNumber, defaultValue = 0) {
        checkTypeUnsignedInt32(defaultValue);
        return this.getFieldWithDefault_(
            fieldNumber, defaultValue,
            (indexArray, bytes) => readOptional(
                indexArray, bytes, reader.readFixed32, WireType.FIXED32));
    }

    /**
     * Returns a fixed64 for the given field number.
     * Note: Since g.m.Long does not support unsigned int64 values we are going
     * the Java route here for now and simply output the number as a signed int64.
     * Users can get to individual bits by themselves.
     * @param {number} fieldNumber
     * @param {!Int64=} defaultValue
     * @return {!Int64}
     */
    getFixed64WithDefault(fieldNumber, defaultValue = Int64.getZero()) {
        return this.getSfixed64WithDefault(fieldNumber, defaultValue);
    }

    /**
     * Returns a float for the given field number.
     * If no default is given zero as the default.
     * @param {number} fieldNumber
     * @param {number=} defaultValue
     * @return {number}
     */
    getFloatWithDefault(fieldNumber, defaultValue = 0) {
        checkTypeFloat(defaultValue);
        return this.getFieldWithDefault_(
            fieldNumber, defaultValue,
            (indexArray, bytes) => readOptional(
                indexArray, bytes, reader.readFloat, WireType.FIXED32));
    }

    /**
     * Returns a int32 for the given field number.
     * If no default is given zero as the default.
     * @param {number} fieldNumber
     * @param {number=} defaultValue
     * @return {number}
     */
    getInt32WithDefault(fieldNumber, defaultValue = 0) {
        checkTypeSignedInt32(defaultValue);
        return this.getFieldWithDefault_(
            fieldNumber, defaultValue,
            (indexArray, bytes) =>
                readOptional(indexArray, bytes, reader.readInt32, WireType.VARINT));
    }

    /**
     * Returns a int64 for the given field number.
     * If no default is given zero as the default.
     * @param {number} fieldNumber
     * @param {!Int64=} defaultValue
     * @return {!Int64}
     */
    getInt64WithDefault(fieldNumber, defaultValue = Int64.getZero()) {
        checkTypeSignedInt64(defaultValue);
        return this.getFieldWithDefault_(
            fieldNumber, defaultValue,
            (indexArray, bytes) =>
                readOptional(indexArray, bytes, reader.readInt64, WireType.VARINT));
    }

    /**
     * Returns a sfixed32 for the given field number.
     * If no default is given zero as the default.
     * @param {number} fieldNumber
     * @param {number=} defaultValue
     * @return {number}
     */
    getSfixed32WithDefault(fieldNumber, defaultValue = 0) {
        checkTypeSignedInt32(defaultValue);
        return this.getFieldWithDefault_(
            fieldNumber, defaultValue,
            (indexArray, bytes) => readOptional(
                indexArray, bytes, reader.readSfixed32, WireType.FIXED32));
    }

    /**
     * Returns a sfixed64 for the given field number.
     * If no default is given zero as the default.
     * @param {number} fieldNumber
     * @param {!Int64=} defaultValue
     * @return {!Int64}
     */
    getSfixed64WithDefault(fieldNumber, defaultValue = Int64.getZero()) {
        checkTypeSignedInt64(defaultValue);
        return this.getFieldWithDefault_(
            fieldNumber, defaultValue,
            (indexArray, bytes) => readOptional(
                indexArray, bytes, reader.readSfixed64, WireType.FIXED64));
    }

    /**
     * Returns a sint32 for the given field number.
     * If no default is given zero as the default.
     * @param {number} fieldNumber
     * @param {number=} defaultValue
     * @return {number}
     */
    getSint32WithDefault(fieldNumber, defaultValue = 0) {
        checkTypeSignedInt32(defaultValue);
        return this.getFieldWithDefault_(
            fieldNumber, defaultValue,
            (indexArray, bytes) => readOptional(
                indexArray, bytes, reader.readSint32, WireType.VARINT));
    }

    /**
     * Returns a sint64 for the given field number.
     * If no default is given zero as the default.
     * @param {number} fieldNumber
     * @param {!Int64=} defaultValue
     * @return {!Int64}
     */
    getSint64WithDefault(fieldNumber, defaultValue = Int64.getZero()) {
        checkTypeSignedInt64(defaultValue);
        return this.getFieldWithDefault_(
            fieldNumber, defaultValue,
            (indexArray, bytes) => readOptional(
                indexArray, bytes, reader.readSint64, WireType.VARINT));
    }

    /**
     * Returns a string for the given field number.
     * If no default is given uses empty string as the default.
     * @param {number} fieldNumber
     * @param {string=} defaultValue
     * @return {string}
     */
    getStringWithDefault(fieldNumber, defaultValue = '') {
        return this.getFieldWithDefault_(
            fieldNumber, defaultValue,
            (indexArray, bytes) => readOptional(
                indexArray, bytes, reader.readString, WireType.DELIMITED));
    }

    /**
     * Returns a uint32 for the given field number.
     * If no default is given zero as the default.
     * @param {number} fieldNumber
     * @param {number=} defaultValue
     * @return {number}
     */
    getUint32WithDefault(fieldNumber, defaultValue = 0) {
        checkTypeUnsignedInt32(defaultValue);
        return this.getFieldWithDefault_(
            fieldNumber, defaultValue,
            (indexArray, bytes) => readOptional(
                indexArray, bytes, reader.readUint32, WireType.VARINT));
    }

    /**
     * Returns a uint64 for the given field number.
     * Note: Since g.m.Long does not support unsigned int64 values we are going
     * the Java route here for now and simply output the number as a signed int64.
     * Users can get to individual bits by themselves.
     * @param {number} fieldNumber
     * @param {!Int64=} defaultValue
     * @return {!Int64}
     */
    getUint64WithDefault(fieldNumber, defaultValue = Int64.getZero()) {
        return this.getInt64WithDefault(fieldNumber, defaultValue);
    }

    /**
     * Returns data as a mutable proto Message for the given field number.
     * If no value has been set, return null.
     * If hasFieldNumber(fieldNumber) == false before calling, it remains false.
     *
     * This method should not be used along with getMessage, since calling
     * getMessageOrNull after getMessage will not register the encoder.
     *
     * @param {number} fieldNumber
     * @param {function(!Kernel):T} instanceCreator
     * @param {number=} pivot
     * @return {?T}
     * @template T
     */
    getMessageOrNull(fieldNumber, instanceCreator, pivot = undefined) {
        return this.getFieldWithDefault_(
            fieldNumber, null,
            (indexArray, bytes) =>
                readMessage(indexArray, bytes, instanceCreator, pivot),
            writeMessage);
    }

    /**
     * Returns data as a mutable proto Message for the given field number.
     * If no value has been set, return null.
     * If hasFieldNumber(fieldNumber) == false before calling, it remains false.
     *
     * This method should not be used along with getMessage, since calling
     * getMessageOrNull after getMessage will not register the encoder.
     *
     * @param {number} fieldNumber
     * @param {function(!Kernel):T} instanceCreator
     * @param {number=} pivot
     * @return {?T}
     * @template T
     */
    getGroupOrNull(fieldNumber, instanceCreator, pivot = undefined) {
        return this.getFieldWithDefault_(
            fieldNumber, null,
            (indexArray, bytes) =>
                readGroup(indexArray, bytes, fieldNumber, instanceCreator, pivot),
            writeGroup);
    }

    /**
     * Returns data as a mutable proto Message for the given field number.
     * If no value has been set previously, creates and attaches an instance.
     * Postcondition: hasFieldNumber(fieldNumber) == true.
     *
     * This method should not be used along with getMessage, since calling
     * getMessageAttach after getMessage will not register the encoder.
     *
     * @param {number} fieldNumber
     * @param {function(!Kernel):T} instanceCreator
     * @param {number=} pivot
     * @return {T}
     * @template T
     */
    getMessageAttach(fieldNumber, instanceCreator, pivot = undefined) {
        checkInstanceCreator(instanceCreator);
        let instance = this.getMessageOrNull(fieldNumber, instanceCreator, pivot);
        if (!instance) {
            instance = instanceCreator(Kernel.createEmpty());
            this.setField_(fieldNumber, instance, writeMessage);
        }
        return instance;
    }

    /**
     * Returns data as a mutable proto Message for the given field number.
     * If no value has been set previously, creates and attaches an instance.
     * Postcondition: hasFieldNumber(fieldNumber) == true.
     *
     * This method should not be used along with getMessage, since calling
     * getMessageAttach after getMessage will not register the encoder.
     *
     * @param {number} fieldNumber
     * @param {function(!Kernel):T} instanceCreator
     * @param {number=} pivot
     * @return {T}
     * @template T
     */
    getGroupAttach(fieldNumber, instanceCreator, pivot = undefined) {
        checkInstanceCreator(instanceCreator);
        let instance = this.getGroupOrNull(fieldNumber, instanceCreator, pivot);
        if (!instance) {
            instance = instanceCreator(Kernel.createEmpty());
            this.setField_(fieldNumber, instance, writeGroup);
        }
        return instance;
    }

    /**
     * Returns data as a proto Message for the given field number.
     * If no value has been set, return a default instance.
     * This default instance is guaranteed to be the same instance, unless this
     * field is cleared.
     * Does not register the encoder, so changes made to the returned
     * sub-message will not be included when serializing the parent message.
     * Use getMessageAttach() if the resulting sub-message should be mutable.
     *
     * This method should not be used along with getMessageOrNull or
     * getMessageAttach, since these methods register the encoder.
     *
     * @param {number} fieldNumber
     * @param {function(!Kernel):T} instanceCreator
     * @param {number=} pivot
     * @return {T}
     * @template T
     */
    getMessage(fieldNumber, instanceCreator, pivot = undefined) {
        checkInstanceCreator(instanceCreator);
        const message = this.getFieldWithDefault_(
            fieldNumber, null,
            (indexArray, bytes) =>
                readMessage(indexArray, bytes, instanceCreator, pivot));
        // Returns an empty message as the default value if the field doesn't exist.
        // We don't pass the default value to getFieldWithDefault_ to reduce object
        // allocation.
        return message === null ? instanceCreator(Kernel.createEmpty()) : message;
    }

    /**
     * Returns data as a proto Message for the given field number.
     * If no value has been set, return a default instance.
     * This default instance is guaranteed to be the same instance, unless this
     * field is cleared.
     * Does not register the encoder, so changes made to the returned
     * sub-message will not be included when serializing the parent message.
     * Use getMessageAttach() if the resulting sub-message should be mutable.
     *
     * This method should not be used along with getMessageOrNull or
     * getMessageAttach, since these methods register the encoder.
     *
     * @param {number} fieldNumber
     * @param {function(!Kernel):T} instanceCreator
     * @param {number=} pivot
     * @return {T}
     * @template T
     */
    getGroup(fieldNumber, instanceCreator, pivot = undefined) {
        checkInstanceCreator(instanceCreator);
        const message = this.getFieldWithDefault_(
            fieldNumber, null,
            (indexArray, bytes) =>
                readGroup(indexArray, bytes, fieldNumber, instanceCreator, pivot));
        // Returns an empty message as the default value if the field doesn't exist.
        // We don't pass the default value to getFieldWithDefault_ to reduce object
        // allocation.
        return message === null ? instanceCreator(Kernel.createEmpty()) : message;
    }

    /**
     * Returns the accessor for the given singular message, or returns null if
     * it hasn't been set.
     * @param {number} fieldNumber
     * @param {number=} pivot
     * @return {?Kernel}
     */
    getMessageAccessorOrNull(fieldNumber, pivot = undefined) {
        checkFieldNumber(fieldNumber);
        const field = this.fields_.get(fieldNumber);
        if (field === undefined) {
            return null;
        }

        if (field.hasDecodedValue()) {
            return checkIsInternalMessage(field.getDecodedValue())
                .internalGetKernel();
        } else {
            return readAccessor(
                checkDefAndNotNull(field.getIndexArray()),
                checkDefAndNotNull(this.bufferDecoder_), pivot);
        }
    }

    /***************************************************************************
     *                        REPEATED GETTER METHODS
     ***************************************************************************/

    /* Bool */

    /**
     * Returns an Array instance containing boolean values for the given field
     * number.
     * @param {number} fieldNumber
     * @return {!Array<boolean>}
     * @private
     */
    getRepeatedBoolArray_(fieldNumber) {
        return this.getFieldWithDefault_(
            fieldNumber, /* defaultValue= */[],
            (indexArray, bytes) => readRepeatedPrimitive(
                indexArray, bytes, reader.readBool, reader.readPackedBool,
                WireType.VARINT));
    }

    /**
     * Returns the element at index for the given field number.
     * @param {number} fieldNumber
     * @param {number} index
     * @return {boolean}
     */
    getRepeatedBoolElement(fieldNumber, index) {
        const array = this.getRepeatedBoolArray_(fieldNumber);
        checkCriticalElementIndex(index, array.length);
        return array[index];
    }

    /**
     * Returns an Iterable instance containing boolean values for the given field
     * number.
     * @param {number} fieldNumber
     * @return {!Iterable<boolean>}
     */
    getRepeatedBoolIterable(fieldNumber) {
        // Don't split this statement unless needed. JS compiler thinks
        // getRepeatedBoolArray_ might have side effects and doesn't inline the
        // call in the compiled code. See cl/293894484 for details.
        return new ArrayIterable(this.getRepeatedBoolArray_(fieldNumber));
    }

    /**
     * Returns the size of the repeated field.
     * @param {number} fieldNumber
     * @return {number}
     */
    getRepeatedBoolSize(fieldNumber) {
        return this.getRepeatedBoolArray_(fieldNumber).length;
    }

    /* Double */

    /**
     * Returns an Array instance containing double values for the given field
     * number.
     * @param {number} fieldNumber
     * @return {!Array<number>}
     * @private
     */
    getRepeatedDoubleArray_(fieldNumber) {
        return this.getFieldWithDefault_(
            fieldNumber, /* defaultValue= */[],
            (indexArray, bytes) => readRepeatedPrimitive(
                indexArray, bytes, reader.readDouble, reader.readPackedDouble,
                WireType.FIXED64));
    }

    /**
     * Returns the element at index for the given field number.
     * @param {number} fieldNumber
     * @param {number} index
     * @return {number}
     */
    getRepeatedDoubleElement(fieldNumber, index) {
        const array = this.getRepeatedDoubleArray_(fieldNumber);
        checkCriticalElementIndex(index, array.length);
        return array[index];
    }

    /**
     * Returns an Iterable instance containing double values for the given field
     * number.
     * @param {number} fieldNumber
     * @return {!Iterable<number>}
     */
    getRepeatedDoubleIterable(fieldNumber) {
        // Don't split this statement unless needed. JS compiler thinks
        // getRepeatedDoubleArray_ might have side effects and doesn't inline the
        // call in the compiled code. See cl/293894484 for details.
        return new ArrayIterable(this.getRepeatedDoubleArray_(fieldNumber));
    }

    /**
     * Returns the size of the repeated field.
     * @param {number} fieldNumber
     * @return {number}
     */
    getRepeatedDoubleSize(fieldNumber) {
        return this.getRepeatedDoubleArray_(fieldNumber).length;
    }

    /* Fixed32 */

    /**
     * Returns an Array instance containing fixed32 values for the given field
     * number.
     * @param {number} fieldNumber
     * @return {!Array<number>}
     * @private
     */
    getRepeatedFixed32Array_(fieldNumber) {
        return this.getFieldWithDefault_(
            fieldNumber, /* defaultValue= */[],
            (indexArray, bytes) => readRepeatedPrimitive(
                indexArray, bytes, reader.readFixed32, reader.readPackedFixed32,
                WireType.FIXED32));
    }

    /**
     * Returns the element at index for the given field number.
     * @param {number} fieldNumber
     * @param {number} index
     * @return {number}
     */
    getRepeatedFixed32Element(fieldNumber, index) {
        const array = this.getRepeatedFixed32Array_(fieldNumber);
        checkCriticalElementIndex(index, array.length);
        return array[index];
    }

    /**
     * Returns an Iterable instance containing fixed32 values for the given field
     * number.
     * @param {number} fieldNumber
     * @return {!Iterable<number>}
     */
    getRepeatedFixed32Iterable(fieldNumber) {
        // Don't split this statement unless needed. JS compiler thinks
        // getRepeatedFixed32Array_ might have side effects and doesn't inline the
        // call in the compiled code. See cl/293894484 for details.
        return new ArrayIterable(this.getRepeatedFixed32Array_(fieldNumber));
    }

    /**
     * Returns the size of the repeated field.
     * @param {number} fieldNumber
     * @return {number}
     */
    getRepeatedFixed32Size(fieldNumber) {
        return this.getRepeatedFixed32Array_(fieldNumber).length;
    }

    /* Fixed64 */

    /**
     * Returns the element at index for the given field number.
     * @param {number} fieldNumber
     * @param {number} index
     * @return {!Int64}
     */
    getRepeatedFixed64Element(fieldNumber, index) {
        return this.getRepeatedSfixed64Element(fieldNumber, index);
    }

    /**
     * Returns an Iterable instance containing fixed64 values for the given field
     * number.
     * @param {number} fieldNumber
     * @return {!Iterable<!Int64>}
     */
    getRepeatedFixed64Iterable(fieldNumber) {
        return this.getRepeatedSfixed64Iterable(fieldNumber);
    }

    /**
     * Returns the size of the repeated field.
     * @param {number} fieldNumber
     * @return {number}
     */
    getRepeatedFixed64Size(fieldNumber) {
        return this.getRepeatedSfixed64Size(fieldNumber);
    }

    /* Float */

    /**
     * Returns an Array instance containing float values for the given field
     * number.
     * @param {number} fieldNumber
     * @return {!Array<number>}
     * @private
     */
    getRepeatedFloatArray_(fieldNumber) {
        return this.getFieldWithDefault_(
            fieldNumber, /* defaultValue= */[],
            (indexArray, bytes) => readRepeatedPrimitive(
                indexArray, bytes, reader.readFloat, reader.readPackedFloat,
                WireType.FIXED32));
    }

    /**
     * Returns the element at index for the given field number.
     * @param {number} fieldNumber
     * @param {number} index
     * @return {number}
     */
    getRepeatedFloatElement(fieldNumber, index) {
        const array = this.getRepeatedFloatArray_(fieldNumber);
        checkCriticalElementIndex(index, array.length);
        return array[index];
    }

    /**
     * Returns an Iterable instance containing float values for the given field
     * number.
     * @param {number} fieldNumber
     * @return {!Iterable<number>}
     */
    getRepeatedFloatIterable(fieldNumber) {
        // Don't split this statement unless needed. JS compiler thinks
        // getRepeatedFloatArray_ might have side effects and doesn't inline the
        // call in the compiled code. See cl/293894484 for details.
        return new ArrayIterable(this.getRepeatedFloatArray_(fieldNumber));
    }

    /**
     * Returns the size of the repeated field.
     * @param {number} fieldNumber
     * @return {number}
     */
    getRepeatedFloatSize(fieldNumber) {
        return this.getRepeatedFloatArray_(fieldNumber).length;
    }

    /* Int32 */

    /**
     * Returns an Array instance containing int32 values for the given field
     * number.
     * @param {number} fieldNumber
     * @return {!Array<number>}
     * @private
     */
    getRepeatedInt32Array_(fieldNumber) {
        return this.getFieldWithDefault_(
            fieldNumber, /* defaultValue= */[],
            (indexArray, bytes) => readRepeatedPrimitive(
                indexArray, bytes, reader.readInt32, reader.readPackedInt32,
                WireType.VARINT));
    }

    /**
     * Returns the element at index for the given field number.
     * @param {number} fieldNumber
     * @param {number} index
     * @return {number}
     */
    getRepeatedInt32Element(fieldNumber, index) {
        const array = this.getRepeatedInt32Array_(fieldNumber);
        checkCriticalElementIndex(index, array.length);
        return array[index];
    }

    /**
     * Returns an Iterable instance containing int32 values for the given field
     * number.
     * @param {number} fieldNumber
     * @return {!Iterable<number>}
     */
    getRepeatedInt32Iterable(fieldNumber) {
        // Don't split this statement unless needed. JS compiler thinks
        // getRepeatedInt32Array_ might have side effects and doesn't inline the
        // call in the compiled code. See cl/293894484 for details.
        return new ArrayIterable(this.getRepeatedInt32Array_(fieldNumber));
    }

    /**
     * Returns the size of the repeated field.
     * @param {number} fieldNumber
     * @return {number}
     */
    getRepeatedInt32Size(fieldNumber) {
        return this.getRepeatedInt32Array_(fieldNumber).length;
    }

    /* Int64 */

    /**
     * Returns an Array instance containing int64 values for the given field
     * number.
     * @param {number} fieldNumber
     * @return {!Array<!Int64>}
     * @private
     */
    getRepeatedInt64Array_(fieldNumber) {
        return this.getFieldWithDefault_(
            fieldNumber, /* defaultValue= */[],
            (indexArray, bytes) => readRepeatedPrimitive(
                indexArray, bytes, reader.readInt64, reader.readPackedInt64,
                WireType.VARINT));
    }

    /**
     * Returns the element at index for the given field number.
     * @param {number} fieldNumber
     * @param {number} index
     * @return {!Int64}
     */
    getRepeatedInt64Element(fieldNumber, index) {
        const array = this.getRepeatedInt64Array_(fieldNumber);
        checkCriticalElementIndex(index, array.length);
        return array[index];
    }

    /**
     * Returns an Iterable instance containing int64 values for the given field
     * number.
     * @param {number} fieldNumber
     * @return {!Iterable<!Int64>}
     */
    getRepeatedInt64Iterable(fieldNumber) {
        // Don't split this statement unless needed. JS compiler thinks
        // getRepeatedInt64Array_ might have side effects and doesn't inline the
        // call in the compiled code. See cl/293894484 for details.
        return new ArrayIterable(this.getRepeatedInt64Array_(fieldNumber));
    }

    /**
     * Returns the size of the repeated field.
     * @param {number} fieldNumber
     * @return {number}
     */
    getRepeatedInt64Size(fieldNumber) {
        return this.getRepeatedInt64Array_(fieldNumber).length;
    }

    /* Sfixed32 */

    /**
     * Returns an Array instance containing sfixed32 values for the given field
     * number.
     * @param {number} fieldNumber
     * @return {!Array<number>}
     * @private
     */
    getRepeatedSfixed32Array_(fieldNumber) {
        return this.getFieldWithDefault_(
            fieldNumber, /* defaultValue= */[],
            (indexArray, bytes) => readRepeatedPrimitive(
                indexArray, bytes, reader.readSfixed32, reader.readPackedSfixed32,
                WireType.FIXED32));
    }

    /**
     * Returns the element at index for the given field number.
     * @param {number} fieldNumber
     * @param {number} index
     * @return {number}
     */
    getRepeatedSfixed32Element(fieldNumber, index) {
        const array = this.getRepeatedSfixed32Array_(fieldNumber);
        checkCriticalElementIndex(index, array.length);
        return array[index];
    }

    /**
     * Returns an Iterable instance containing sfixed32 values for the given field
     * number.
     * @param {number} fieldNumber
     * @return {!Iterable<number>}
     */
    getRepeatedSfixed32Iterable(fieldNumber) {
        // Don't split this statement unless needed. JS compiler thinks
        // getRepeatedSfixed32Array_ might have side effects and doesn't inline the
        // call in the compiled code. See cl/293894484 for details.
        return new ArrayIterable(this.getRepeatedSfixed32Array_(fieldNumber));
    }

    /**
     * Returns the size of the repeated field.
     * @param {number} fieldNumber
     * @return {number}
     */
    getRepeatedSfixed32Size(fieldNumber) {
        return this.getRepeatedSfixed32Array_(fieldNumber).length;
    }

    /* Sfixed64 */

    /**
     * Returns an Array instance containing sfixed64 values for the given field
     * number.
     * @param {number} fieldNumber
     * @return {!Array<!Int64>}
     * @private
     */
    getRepeatedSfixed64Array_(fieldNumber) {
        return this.getFieldWithDefault_(
            fieldNumber, /* defaultValue= */[],
            (indexArray, bytes) => readRepeatedPrimitive(
                indexArray, bytes, reader.readSfixed64, reader.readPackedSfixed64,
                WireType.FIXED64));
    }

    /**
     * Returns the element at index for the given field number.
     * @param {number} fieldNumber
     * @param {number} index
     * @return {!Int64}
     */
    getRepeatedSfixed64Element(fieldNumber, index) {
        const array = this.getRepeatedSfixed64Array_(fieldNumber);
        checkCriticalElementIndex(index, array.length);
        return array[index];
    }

    /**
     * Returns an Iterable instance containing sfixed64 values for the given field
     * number.
     * @param {number} fieldNumber
     * @return {!Iterable<!Int64>}
     */
    getRepeatedSfixed64Iterable(fieldNumber) {
        // Don't split this statement unless needed. JS compiler thinks
        // getRepeatedSfixed64Array_ might have side effects and doesn't inline the
        // call in the compiled code. See cl/293894484 for details.
        return new ArrayIterable(this.getRepeatedSfixed64Array_(fieldNumber));
    }

    /**
     * Returns the size of the repeated field.
     * @param {number} fieldNumber
     * @return {number}
     */
    getRepeatedSfixed64Size(fieldNumber) {
        return this.getRepeatedSfixed64Array_(fieldNumber).length;
    }

    /* Sint32 */

    /**
     * Returns an Array instance containing sint32 values for the given field
     * number.
     * @param {number} fieldNumber
     * @return {!Array<number>}
     * @private
     */
    getRepeatedSint32Array_(fieldNumber) {
        return this.getFieldWithDefault_(
            fieldNumber, /* defaultValue= */[],
            (indexArray, bytes) => readRepeatedPrimitive(
                indexArray, bytes, reader.readSint32, reader.readPackedSint32,
                WireType.VARINT));
    }

    /**
     * Returns the element at index for the given field number.
     * @param {number} fieldNumber
     * @param {number} index
     * @return {number}
     */
    getRepeatedSint32Element(fieldNumber, index) {
        const array = this.getRepeatedSint32Array_(fieldNumber);
        checkCriticalElementIndex(index, array.length);
        return array[index];
    }

    /**
     * Returns an Iterable instance containing sint32 values for the given field
     * number.
     * @param {number} fieldNumber
     * @return {!Iterable<number>}
     */
    getRepeatedSint32Iterable(fieldNumber) {
        // Don't split this statement unless needed. JS compiler thinks
        // getRepeatedSint32Array_ might have side effects and doesn't inline the
        // call in the compiled code. See cl/293894484 for details.
        return new ArrayIterable(this.getRepeatedSint32Array_(fieldNumber));
    }

    /**
     * Returns the size of the repeated field.
     * @param {number} fieldNumber
     * @return {number}
     */
    getRepeatedSint32Size(fieldNumber) {
        return this.getRepeatedSint32Array_(fieldNumber).length;
    }

    /* Sint64 */

    /**
     * Returns an Array instance containing sint64 values for the given field
     * number.
     * @param {number} fieldNumber
     * @return {!Array<!Int64>}
     * @private
     */
    getRepeatedSint64Array_(fieldNumber) {
        return this.getFieldWithDefault_(
            fieldNumber, /* defaultValue= */[],
            (indexArray, bytes) => readRepeatedPrimitive(
                indexArray, bytes, reader.readSint64, reader.readPackedSint64,
                WireType.VARINT));
    }

    /**
     * Returns the element at index for the given field number.
     * @param {number} fieldNumber
     * @param {number} index
     * @return {!Int64}
     */
    getRepeatedSint64Element(fieldNumber, index) {
        const array = this.getRepeatedSint64Array_(fieldNumber);
        checkCriticalElementIndex(index, array.length);
        return array[index];
    }

    /**
     * Returns an Iterable instance containing sint64 values for the given field
     * number.
     * @param {number} fieldNumber
     * @return {!Iterable<!Int64>}
     */
    getRepeatedSint64Iterable(fieldNumber) {
        // Don't split this statement unless needed. JS compiler thinks
        // getRepeatedSint64Array_ might have side effects and doesn't inline the
        // call in the compiled code. See cl/293894484 for details.
        return new ArrayIterable(this.getRepeatedSint64Array_(fieldNumber));
    }

    /**
     * Returns the size of the repeated field.
     * @param {number} fieldNumber
     * @return {number}
     */
    getRepeatedSint64Size(fieldNumber) {
        return this.getRepeatedSint64Array_(fieldNumber).length;
    }

    /* Uint32 */

    /**
     * Returns an Array instance containing uint32 values for the given field
     * number.
     * @param {number} fieldNumber
     * @return {!Array<number>}
     * @private
     */
    getRepeatedUint32Array_(fieldNumber) {
        return this.getFieldWithDefault_(
            fieldNumber, /* defaultValue= */[],
            (indexArray, bytes) => readRepeatedPrimitive(
                indexArray, bytes, reader.readUint32, reader.readPackedUint32,
                WireType.VARINT));
    }

    /**
     * Returns the element at index for the given field number.
     * @param {number} fieldNumber
     * @param {number} index
     * @return {number}
     */
    getRepeatedUint32Element(fieldNumber, index) {
        const array = this.getRepeatedUint32Array_(fieldNumber);
        checkCriticalElementIndex(index, array.length);
        return array[index];
    }

    /**
     * Returns an Iterable instance containing uint32 values for the given field
     * number.
     * @param {number} fieldNumber
     * @return {!Iterable<number>}
     */
    getRepeatedUint32Iterable(fieldNumber) {
        // Don't split this statement unless needed. JS compiler thinks
        // getRepeatedUint32Array_ might have side effects and doesn't inline the
        // call in the compiled code. See cl/293894484 for details.
        return new ArrayIterable(this.getRepeatedUint32Array_(fieldNumber));
    }

    /**
     * Returns the size of the repeated field.
     * @param {number} fieldNumber
     * @return {number}
     */
    getRepeatedUint32Size(fieldNumber) {
        return this.getRepeatedUint32Array_(fieldNumber).length;
    }

    /* Uint64 */

    /**
     * Returns the element at index for the given field number.
     * @param {number} fieldNumber
     * @param {number} index
     * @return {!Int64}
     */
    getRepeatedUint64Element(fieldNumber, index) {
        return this.getRepeatedInt64Element(fieldNumber, index);
    }

    /**
     * Returns an Iterable instance containing uint64 values for the given field
     * number.
     * @param {number} fieldNumber
     * @return {!Iterable<!Int64>}
     */
    getRepeatedUint64Iterable(fieldNumber) {
        return this.getRepeatedInt64Iterable(fieldNumber);
    }

    /**
     * Returns the size of the repeated field.
     * @param {number} fieldNumber
     * @return {number}
     */
    getRepeatedUint64Size(fieldNumber) {
        return this.getRepeatedInt64Size(fieldNumber);
    }

    /* Bytes */

    /**
     * Returns an array instance containing bytes values for the given field
     * number.
     * @param {number} fieldNumber
     * @return {!Array<!ByteString>}
     * @private
     */
    getRepeatedBytesArray_(fieldNumber) {
        return this.getFieldWithDefault_(
            fieldNumber, /* defaultValue= */[],
            (indexArray, bytes) =>
                readRepeatedNonPrimitive(indexArray, bytes, reader.readBytes));
    }

    /**
     * Returns the element at index for the given field number as a bytes.
     * @param {number} fieldNumber
     * @param {number} index
     * @return {!ByteString}
     */
    getRepeatedBytesElement(fieldNumber, index) {
        const array = this.getRepeatedBytesArray_(fieldNumber);
        checkCriticalElementIndex(index, array.length);
        return array[index];
    }

    /**
     * Returns an Iterable instance containing bytes values for the given field
     * number.
     * @param {number} fieldNumber
     * @return {!Iterable<!ByteString>}
     */
    getRepeatedBytesIterable(fieldNumber) {
        // Don't split this statement unless needed. JS compiler thinks
        // getRepeatedBytesArray_ might have side effects and doesn't inline the
        // call in the compiled code. See cl/293894484 for details.
        return new ArrayIterable(this.getRepeatedBytesArray_(fieldNumber));
    }

    /**
     * Returns the size of the repeated field.
     * @param {number} fieldNumber
     * @return {number}
     */
    getRepeatedBytesSize(fieldNumber) {
        return this.getRepeatedBytesArray_(fieldNumber).length;
    }

    /* String */

    /**
     * Returns an array instance containing string values for the given field
     * number.
     * @param {number} fieldNumber
     * @return {!Array<string>}
     * @private
     */
    getRepeatedStringArray_(fieldNumber) {
        return this.getFieldWithDefault_(
            fieldNumber, /* defaultValue= */[],
            (indexArray, bufferDecoder) => readRepeatedNonPrimitive(
                indexArray, bufferDecoder, reader.readString));
    }

    /**
     * Returns the element at index for the given field number as a string.
     * @param {number} fieldNumber
     * @param {number} index
     * @return {string}
     */
    getRepeatedStringElement(fieldNumber, index) {
        const array = this.getRepeatedStringArray_(fieldNumber);
        checkCriticalElementIndex(index, array.length);
        return array[index];
    }

    /**
     * Returns an Iterable instance containing string values for the given field
     * number.
     * @param {number} fieldNumber
     * @return {!Iterable<string>}
     */
    getRepeatedStringIterable(fieldNumber) {
        // Don't split this statement unless needed. JS compiler thinks
        // getRepeatedStringArray_ might have side effects and doesn't inline the
        // call in the compiled code. See cl/293894484 for details.
        return new ArrayIterable(this.getRepeatedStringArray_(fieldNumber));
    }

    /**
     * Returns the size of the repeated field.
     * @param {number} fieldNumber
     * @return {number}
     */
    getRepeatedStringSize(fieldNumber) {
        return this.getRepeatedStringArray_(fieldNumber).length;
    }

    /* Message */

    /**
     * Returns an Array instance containing boolean values for the given field
     * number.
     * @param {number} fieldNumber
     * @param {function(!Kernel):T} instanceCreator
     * @param {number|undefined} pivot
     * @return {!Array<T>}
     * @template T
     * @private
     */
    getRepeatedMessageArray_(fieldNumber, instanceCreator, pivot) {
        // This method can be shortened using getFieldWithDefault and
        // getRepeatedNonPrimitive methods. But that will require creating and
        // passing a reader closure every time getRepeatedMessageArray_ is called,
        // which is expensive.
        checkInstanceCreator(instanceCreator);
        checkFieldNumber(fieldNumber);

        const field = this.fields_.get(fieldNumber);
        if (field === undefined) {
            return [];
        }

        if (field.hasDecodedValue()) {
            return field.getDecodedValue();
        }

        const indexArray = checkDefAndNotNull(field.getIndexArray());
        const result = new Array(indexArray.length);
        for (let i = 0; i < indexArray.length; i++) {
            validateWireType(indexArray[i], WireType.DELIMITED);
            const subMessageBuffer = reader.readDelimited(
                checkDefAndNotNull(this.bufferDecoder_),
                Field.getStartIndex(indexArray[i]));
            result[i] =
                instanceCreator(Kernel.fromBufferDecoder_(subMessageBuffer, pivot));
        }
        field.setCache(result, writeRepeatedMessage);

        return result;
    }

    /**
     * Returns the element at index for the given field number as a message.
     * @param {number} fieldNumber
     * @param {function(!Kernel):T} instanceCreator
     * @param {number} index
     * @param {number=} pivot
     * @return {T}
     * @template T
     */
    getRepeatedMessageElement(
        fieldNumber, instanceCreator, index, pivot = undefined) {
        const array =
            this.getRepeatedMessageArray_(fieldNumber, instanceCreator, pivot);
        checkCriticalElementIndex(index, array.length);
        return array[index];
    }

    /**
     * Returns an Iterable instance containing message values for the given field
     * number.
     * @param {number} fieldNumber
     * @param {function(!Kernel):T} instanceCreator
     * @param {number=} pivot
     * @return {!Iterable<T>}
     * @template T
     */
    getRepeatedMessageIterable(fieldNumber, instanceCreator, pivot = undefined) {
        // Don't split this statement unless needed. JS compiler thinks
        // getRepeatedMessageArray_ might have side effects and doesn't inline the
        // call in the compiled code. See cl/293894484 for details.
        return new ArrayIterable(
            this.getRepeatedMessageArray_(fieldNumber, instanceCreator, pivot));
    }

    /**
     * Returns an Iterable instance containing message accessors for the given
     * field number.
     * @param {number} fieldNumber
     * @param {number=} pivot
     * @return {!Iterable<!Kernel>}
     */
    getRepeatedMessageAccessorIterable(fieldNumber, pivot = undefined) {
        checkFieldNumber(fieldNumber);

        const field = this.fields_.get(fieldNumber);
        if (!field) {
            return [];
        }

        if (field.hasDecodedValue()) {
            return new ArrayIterable(field.getDecodedValue().map(
                value => checkIsInternalMessage(value).internalGetKernel()));
        }

        const readMessageFunc = (bufferDecoder, start) => Kernel.fromBufferDecoder_(
            reader.readDelimited(bufferDecoder, start), pivot);
        const array = readRepeatedNonPrimitive(
            checkDefAndNotNull(field.getIndexArray()),
            checkDefAndNotNull(this.bufferDecoder_), readMessageFunc);
        return new ArrayIterable(array);
    }

    /**
     * Returns the size of the repeated field.
     * @param {number} fieldNumber
     * @param {function(!Kernel):T} instanceCreator
     * @return {number}
     * @param {number=} pivot
     * @template T
     */
    getRepeatedMessageSize(fieldNumber, instanceCreator, pivot = undefined) {
        return this.getRepeatedMessageArray_(fieldNumber, instanceCreator, pivot)
            .length;
    }

    /**
     * Returns an Array instance containing boolean values for the given field
     * number.
     * @param {number} fieldNumber
     * @param {function(!Kernel):T} instanceCreator
     * @param {number|undefined} pivot
     * @return {!Array<T>}
     * @template T
     * @private
     */
    getRepeatedGroupArray_(fieldNumber, instanceCreator, pivot) {
        return this.getFieldWithDefault_(
            fieldNumber, [],
            (indexArray, bufferDecoder) => readRepeatedGroup(
                indexArray, bufferDecoder, fieldNumber, instanceCreator, pivot),
            writeRepeatedGroup);
    }

    /**
     * Returns the element at index for the given field number as a group.
     * @param {number} fieldNumber
     * @param {function(!Kernel):T} instanceCreator
     * @param {number} index
     * @param {number=} pivot
     * @return {T}
     * @template T
     */
    getRepeatedGroupElement(
        fieldNumber, instanceCreator, index, pivot = undefined) {
        const array =
            this.getRepeatedGroupArray_(fieldNumber, instanceCreator, pivot);
        checkCriticalElementIndex(index, array.length);
        return array[index];
    }

    /**
     * Returns an Iterable instance containing group values for the given field
     * number.
     * @param {number} fieldNumber
     * @param {function(!Kernel):T} instanceCreator
     * @param {number=} pivot
     * @return {!Iterable<T>}
     * @template T
     */
    getRepeatedGroupIterable(fieldNumber, instanceCreator, pivot = undefined) {
        // Don't split this statement unless needed. JS compiler thinks
        // getRepeatedMessageArray_ might have side effects and doesn't inline the
        // call in the compiled code. See cl/293894484 for details.
        return new ArrayIterable(
            this.getRepeatedGroupArray_(fieldNumber, instanceCreator, pivot));
    }

    /**
     * Returns the size of the repeated field.
     * @param {number} fieldNumber
     * @param {function(!Kernel):T} instanceCreator
     * @return {number}
     * @param {number=} pivot
     * @template T
     */
    getRepeatedGroupSize(fieldNumber, instanceCreator, pivot = undefined) {
        return this.getRepeatedGroupArray_(fieldNumber, instanceCreator, pivot)
            .length;
    }

    /***************************************************************************
     *                        OPTIONAL SETTER METHODS
     ***************************************************************************/

    /**
     * Sets a boolean value to the field with the given field number.
     * @param {number} fieldNumber
     * @param {boolean} value
     */
    setBool(fieldNumber, value) {
        checkCriticalTypeBool(value);
        this.setField_(fieldNumber, value, (writer, fieldNumber, value) => {
            writer.writeBool(fieldNumber, value);
        });
    }

    /**
     * Sets a boolean value to the field with the given field number.
     * @param {number} fieldNumber
     * @param {!ByteString} value
     */
    setBytes(fieldNumber, value) {
        checkCriticalTypeByteString(value);
        this.setField_(fieldNumber, value, (writer, fieldNumber, value) => {
            writer.writeBytes(fieldNumber, value);
        });
    }

    /**
     * Sets a double value to the field with the given field number.
     * @param {number} fieldNumber
     * @param {number} value
     */
    setDouble(fieldNumber, value) {
        checkCriticalTypeDouble(value);
        this.setField_(fieldNumber, value, (writer, fieldNumber, value) => {
            writer.writeDouble(fieldNumber, value);
        });
    }

    /**
     * Sets a fixed32 value to the field with the given field number.
     * @param {number} fieldNumber
     * @param {number} value
     */
    setFixed32(fieldNumber, value) {
        checkCriticalTypeUnsignedInt32(value);
        this.setField_(fieldNumber, value, (writer, fieldNumber, value) => {
            writer.writeFixed32(fieldNumber, value);
        });
    }

    /**
     * Sets a uint64 value to the field with the given field number.\
     * Note: Since g.m.Long does not support unsigned int64 values we are going
     * the Java route here for now and simply output the number as a signed int64.
     * Users can get to individual bits by themselves.
     * @param {number} fieldNumber
     * @param {!Int64} value
     */
    setFixed64(fieldNumber, value) {
        this.setSfixed64(fieldNumber, value);
    }

    /**
     * Sets a float value to the field with the given field number.
     * @param {number} fieldNumber
     * @param {number} value
     */
    setFloat(fieldNumber, value) {
        checkCriticalTypeFloat(value);
        // Eagerly round to 32-bit precision so that reading back after set will
        // yield the same value a reader will receive after serialization.
        const floatValue = Math.fround(value);
        this.setField_(fieldNumber, floatValue, (writer, fieldNumber, value) => {
            writer.writeFloat(fieldNumber, value);
        });
    }

    /**
     * Sets a int32 value to the field with the given field number.
     * @param {number} fieldNumber
     * @param {number} value
     */
    setInt32(fieldNumber, value) {
        checkCriticalTypeSignedInt32(value);
        this.setField_(fieldNumber, value, (writer, fieldNumber, value) => {
            writer.writeInt32(fieldNumber, value);
        });
    }

    /**
     * Sets a int64 value to the field with the given field number.
     * @param {number} fieldNumber
     * @param {!Int64} value
     */
    setInt64(fieldNumber, value) {
        checkCriticalTypeSignedInt64(value);
        this.setField_(fieldNumber, value, (writer, fieldNumber, value) => {
            writer.writeInt64(fieldNumber, value);
        });
    }

    /**
     * Sets a sfixed32 value to the field with the given field number.
     * @param {number} fieldNumber
     * @param {number} value
     */
    setSfixed32(fieldNumber, value) {
        checkCriticalTypeSignedInt32(value);
        this.setField_(fieldNumber, value, (writer, fieldNumber, value) => {
            writer.writeSfixed32(fieldNumber, value);
        });
    }

    /**
     * Sets a sfixed64 value to the field with the given field number.
     * @param {number} fieldNumber
     * @param {!Int64} value
     */
    setSfixed64(fieldNumber, value) {
        checkCriticalTypeSignedInt64(value);
        this.setField_(fieldNumber, value, (writer, fieldNumber, value) => {
            writer.writeSfixed64(fieldNumber, value);
        });
    }

    /**
     * Sets a sint32 value to the field with the given field number.
     * @param {number} fieldNumber
     * @param {number} value
     */
    setSint32(fieldNumber, value) {
        checkCriticalTypeSignedInt32(value);
        this.setField_(fieldNumber, value, (writer, fieldNumber, value) => {
            writer.writeSint32(fieldNumber, value);
        });
    }

    /**
     * Sets a sint64 value to the field with the given field number.
     * @param {number} fieldNumber
     * @param {!Int64} value
     */
    setSint64(fieldNumber, value) {
        checkCriticalTypeSignedInt64(value);
        this.setField_(fieldNumber, value, (writer, fieldNumber, value) => {
            writer.writeSint64(fieldNumber, value);
        });
    }

    /**
     * Sets a boolean value to the field with the given field number.
     * @param {number} fieldNumber
     * @param {string} value
     */
    setString(fieldNumber, value) {
        checkCriticalTypeString(value);
        this.setField_(fieldNumber, value, (writer, fieldNumber, value) => {
            writer.writeString(fieldNumber, value);
        });
    }

    /**
     * Sets a uint32 value to the field with the given field number.
     * @param {number} fieldNumber
     * @param {number} value
     */
    setUint32(fieldNumber, value) {
        checkCriticalTypeUnsignedInt32(value);
        this.setField_(fieldNumber, value, (writer, fieldNumber, value) => {
            writer.writeUint32(fieldNumber, value);
        });
    }

    /**
     * Sets a uint64 value to the field with the given field number.\
     * Note: Since g.m.Long does not support unsigned int64 values we are going
     * the Java route here for now and simply output the number as a signed int64.
     * Users can get to individual bits by themselves.
     * @param {number} fieldNumber
     * @param {!Int64} value
     */
    setUint64(fieldNumber, value) {
        this.setInt64(fieldNumber, value);
    }

    /**
     * Sets a proto Group to the field with the given field number.
     * Instead of working with the Kernel inside of the message directly, we
     * need the message instance to keep its reference equality for subsequent
     * gettings.
     * @param {number} fieldNumber
     * @param {!InternalMessage} value
     */
    setGroup(fieldNumber, value) {
        checkCriticalType(
            value !== null, 'Given value is not a message instance: null');
        this.setField_(fieldNumber, value, writeGroup);
    }

    /**
     * Sets a proto Message to the field with the given field number.
     * Instead of working with the Kernel inside of the message directly, we
     * need the message instance to keep its reference equality for subsequent
     * gettings.
     * @param {number} fieldNumber
     * @param {!InternalMessage} value
     */
    setMessage(fieldNumber, value) {
        checkCriticalType(
            value !== null, 'Given value is not a message instance: null');
        this.setField_(fieldNumber, value, writeMessage);
    }

    /***************************************************************************
     *                        REPEATED SETTER METHODS
     ***************************************************************************/

    /* Bool */

    /**
     * Adds all boolean values into the field for the given field number.
     * How these values are encoded depends on the given write function.
     * @param {number} fieldNumber
     * @param {!Iterable<boolean>} values
     * @param {function(!Writer, number, !Array<boolean>): undefined} encoder
     * @private
     */
    addRepeatedBoolIterable_(fieldNumber, values, encoder) {
        const array = [...this.getRepeatedBoolArray_(fieldNumber), ...values];
        checkCriticalTypeBoolArray(array);
        // Needs to set it back because the default empty array was not cached.
        this.setField_(fieldNumber, array, encoder);
    }

    /**
     * Adds a single boolean value into the field for the given field number.
     * All values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {boolean} value
     */
    addPackedBoolElement(fieldNumber, value) {
        this.addRepeatedBoolIterable_(
            fieldNumber, [value], (writer, fieldNumber, values) => {
                writer.writePackedBool(fieldNumber, values);
            });
    }

    /**
     * Adds all boolean values into the field for the given field number.
     * All these values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {!Iterable<boolean>} values
     */
    addPackedBoolIterable(fieldNumber, values) {
        this.addRepeatedBoolIterable_(
            fieldNumber, values, (writer, fieldNumber, values) => {
                writer.writePackedBool(fieldNumber, values);
            });
    }

    /**
     * Adds a single boolean value into the field for the given field number.
     * All values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {boolean} value
     */
    addUnpackedBoolElement(fieldNumber, value) {
        this.addRepeatedBoolIterable_(
            fieldNumber, [value], (writer, fieldNumber, values) => {
                writer.writeRepeatedBool(fieldNumber, values);
            });
    }

    /**
     * Adds all boolean values into the field for the given field number.
     * All these values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {!Iterable<boolean>} values
     */
    addUnpackedBoolIterable(fieldNumber, values) {
        this.addRepeatedBoolIterable_(
            fieldNumber, values, (writer, fieldNumber, values) => {
                writer.writeRepeatedBool(fieldNumber, values);
            });
    }

    /**
     * Sets a single boolean value into the field for the given field number at
     * the given index. How these values are encoded depends on the given write
     * function.
     * @param {number} fieldNumber
     * @param {number} index
     * @param {boolean} value
     * @param {function(!Writer, number, !Array<boolean>): undefined} encoder
     * @throws {!Error} if index is out of range when check mode is critical
     * @private
     */
    setRepeatedBoolElement_(fieldNumber, index, value, encoder) {
        checkCriticalTypeBool(value);
        const array = this.getRepeatedBoolArray_(fieldNumber);
        checkCriticalElementIndex(index, array.length);
        array[index] = value;
        // Needs to set it back to set encoder.
        this.setField_(fieldNumber, array, encoder);
    }

    /**
     * Sets a single boolean value into the field for the given field number at
     * the given index. All values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {number} index
     * @param {boolean} value
     * @throws {!Error} if index is out of range when check mode is critical
     */
    setPackedBoolElement(fieldNumber, index, value) {
        this.setRepeatedBoolElement_(
            fieldNumber, index, value, (writer, fieldNumber, values) => {
                writer.writePackedBool(fieldNumber, values);
            });
    }

    /**
     * Sets all boolean values into the field for the given field number.
     * All these values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {!Iterable<boolean>} values
     */
    setPackedBoolIterable(fieldNumber, values) {
        const /** !Array<boolean> */ array = Array.from(values);
        checkCriticalTypeBoolArray(array);
        this.setField_(fieldNumber, array, (writer, fieldNumber, values) => {
            writer.writePackedBool(fieldNumber, values);
        });
    }

    /**
     * Sets a single boolean value into the field for the given field number at
     * the given index. All values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {number} index
     * @param {boolean} value
     * @throws {!Error} if index is out of range when check mode is critical
     */
    setUnpackedBoolElement(fieldNumber, index, value) {
        this.setRepeatedBoolElement_(
            fieldNumber, index, value, (writer, fieldNumber, values) => {
                writer.writeRepeatedBool(fieldNumber, values);
            });
    }

    /**
     * Sets all boolean values into the field for the given field number.
     * All these values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {!Iterable<boolean>} values
     */
    setUnpackedBoolIterable(fieldNumber, values) {
        const /** !Array<boolean> */ array = Array.from(values);
        checkCriticalTypeBoolArray(array);
        this.setField_(fieldNumber, array, (writer, fieldNumber, values) => {
            writer.writeRepeatedBool(fieldNumber, values);
        });
    }

    /* Double */

    /**
     * Adds all double values into the field for the given field number.
     * How these values are encoded depends on the given write function.
     * @param {number} fieldNumber
     * @param {!Iterable<number>} values
     * @param {function(!Writer, number, !Array<number>): undefined} encoder
     * @private
     */
    addRepeatedDoubleIterable_(fieldNumber, values, encoder) {
        const array = [...this.getRepeatedDoubleArray_(fieldNumber), ...values];
        checkCriticalTypeDoubleArray(array);
        // Needs to set it back because the default empty array was not cached.
        this.setField_(fieldNumber, array, encoder);
    }

    /**
     * Adds a single double value into the field for the given field number.
     * All values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {number} value
     */
    addPackedDoubleElement(fieldNumber, value) {
        this.addRepeatedDoubleIterable_(
            fieldNumber, [value], (writer, fieldNumber, values) => {
                writer.writePackedDouble(fieldNumber, values);
            });
    }

    /**
     * Adds all double values into the field for the given field number.
     * All these values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {!Iterable<number>} values
     */
    addPackedDoubleIterable(fieldNumber, values) {
        this.addRepeatedDoubleIterable_(
            fieldNumber, values, (writer, fieldNumber, values) => {
                writer.writePackedDouble(fieldNumber, values);
            });
    }

    /**
     * Adds a single double value into the field for the given field number.
     * All values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {number} value
     */
    addUnpackedDoubleElement(fieldNumber, value) {
        this.addRepeatedDoubleIterable_(
            fieldNumber, [value], (writer, fieldNumber, values) => {
                writer.writeRepeatedDouble(fieldNumber, values);
            });
    }

    /**
     * Adds all double values into the field for the given field number.
     * All these values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {!Iterable<number>} values
     */
    addUnpackedDoubleIterable(fieldNumber, values) {
        this.addRepeatedDoubleIterable_(
            fieldNumber, values, (writer, fieldNumber, values) => {
                writer.writeRepeatedDouble(fieldNumber, values);
            });
    }

    /**
     * Sets a single double value into the field for the given field number at the
     * given index.
     * How these values are encoded depends on the given write function.
     * @param {number} fieldNumber
     * @param {number} index
     * @param {number} value
     * @param {function(!Writer, number, !Array<number>): undefined} encoder
     * @throws {!Error} if index is out of range when check mode is critical
     * @private
     */
    setRepeatedDoubleElement_(fieldNumber, index, value, encoder) {
        checkCriticalTypeDouble(value);
        const array = this.getRepeatedDoubleArray_(fieldNumber);
        checkCriticalElementIndex(index, array.length);
        array[index] = value;
        // Needs to set it back to set encoder.
        this.setField_(fieldNumber, array, encoder);
    }

    /**
     * Sets a single double value into the field for the given field number at the
     * given index.
     * All values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {number} index
     * @param {number} value
     * @throws {!Error} if index is out of range when check mode is critical
     */
    setPackedDoubleElement(fieldNumber, index, value) {
        this.setRepeatedDoubleElement_(
            fieldNumber, index, value, (writer, fieldNumber, values) => {
                writer.writePackedDouble(fieldNumber, values);
            });
    }

    /**
     * Sets all double values into the field for the given field number.
     * All these values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {!Iterable<number>} values
     */
    setPackedDoubleIterable(fieldNumber, values) {
        const array = Array.from(values);
        checkCriticalTypeDoubleArray(array);
        this.setField_(fieldNumber, array, (writer, fieldNumber, values) => {
            writer.writePackedDouble(fieldNumber, values);
        });
    }

    /**
     * Sets a single double value into the field for the given field number at the
     * given index.
     * All values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {number} index
     * @param {number} value
     * @throws {!Error} if index is out of range when check mode is critical
     */
    setUnpackedDoubleElement(fieldNumber, index, value) {
        this.setRepeatedDoubleElement_(
            fieldNumber, index, value, (writer, fieldNumber, values) => {
                writer.writeRepeatedDouble(fieldNumber, values);
            });
    }

    /**
     * Sets all double values into the field for the given field number.
     * All these values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {!Iterable<number>} values
     */
    setUnpackedDoubleIterable(fieldNumber, values) {
        const array = Array.from(values);
        checkCriticalTypeDoubleArray(array);
        this.setField_(fieldNumber, array, (writer, fieldNumber, values) => {
            writer.writeRepeatedDouble(fieldNumber, values);
        });
    }

    /* Fixed32 */

    /**
     * Adds all fixed32 values into the field for the given field number.
     * How these values are encoded depends on the given write function.
     * @param {number} fieldNumber
     * @param {!Iterable<number>} values
     * @param {function(!Writer, number, !Array<number>): undefined} encoder
     * @private
     */
    addRepeatedFixed32Iterable_(fieldNumber, values, encoder) {
        const array = [...this.getRepeatedFixed32Array_(fieldNumber), ...values];
        checkCriticalTypeUnsignedInt32Array(array);
        // Needs to set it back because the default empty array was not cached.
        this.setField_(fieldNumber, array, encoder);
    }

    /**
     * Adds a single fixed32 value into the field for the given field number.
     * All values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {number} value
     */
    addPackedFixed32Element(fieldNumber, value) {
        this.addRepeatedFixed32Iterable_(
            fieldNumber, [value], (writer, fieldNumber, values) => {
                writer.writePackedFixed32(fieldNumber, values);
            });
    }

    /**
     * Adds all fixed32 values into the field for the given field number.
     * All these values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {!Iterable<number>} values
     */
    addPackedFixed32Iterable(fieldNumber, values) {
        this.addRepeatedFixed32Iterable_(
            fieldNumber, values, (writer, fieldNumber, values) => {
                writer.writePackedFixed32(fieldNumber, values);
            });
    }

    /**
     * Adds a single fixed32 value into the field for the given field number.
     * All values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {number} value
     */
    addUnpackedFixed32Element(fieldNumber, value) {
        this.addRepeatedFixed32Iterable_(
            fieldNumber, [value], (writer, fieldNumber, values) => {
                writer.writeRepeatedFixed32(fieldNumber, values);
            });
    }

    /**
     * Adds all fixed32 values into the field for the given field number.
     * All these values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {!Iterable<number>} values
     */
    addUnpackedFixed32Iterable(fieldNumber, values) {
        this.addRepeatedFixed32Iterable_(
            fieldNumber, values, (writer, fieldNumber, values) => {
                writer.writeRepeatedFixed32(fieldNumber, values);
            });
    }

    /**
     * Sets a single fixed32 value into the field for the given field number at
     * the given index. How these values are encoded depends on the given write
     * function.
     * @param {number} fieldNumber
     * @param {number} index
     * @param {number} value
     * @param {function(!Writer, number, !Array<number>): undefined} encoder
     * @throws {!Error} if index is out of range when check mode is critical
     * @private
     */
    setRepeatedFixed32Element_(fieldNumber, index, value, encoder) {
        checkCriticalTypeUnsignedInt32(value);
        const array = this.getRepeatedFixed32Array_(fieldNumber);
        checkCriticalElementIndex(index, array.length);
        array[index] = value;
        // Needs to set it back to set encoder.
        this.setField_(fieldNumber, array, encoder);
    }

    /**
     * Sets a single fixed32 value into the field for the given field number at
     * the given index. All values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {number} index
     * @param {number} value
     * @throws {!Error} if index is out of range when check mode is critical
     */
    setPackedFixed32Element(fieldNumber, index, value) {
        this.setRepeatedFixed32Element_(
            fieldNumber, index, value, (writer, fieldNumber, values) => {
                writer.writePackedFixed32(fieldNumber, values);
            });
    }

    /**
     * Sets all fixed32 values into the field for the given field number.
     * All these values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {!Iterable<number>} values
     */
    setPackedFixed32Iterable(fieldNumber, values) {
        const array = Array.from(values);
        checkCriticalTypeUnsignedInt32Array(array);
        this.setField_(fieldNumber, array, (writer, fieldNumber, values) => {
            writer.writePackedFixed32(fieldNumber, values);
        });
    }

    /**
     * Sets a single fixed32 value into the field for the given field number at
     * the given index. All values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {number} index
     * @param {number} value
     * @throws {!Error} if index is out of range when check mode is critical
     */
    setUnpackedFixed32Element(fieldNumber, index, value) {
        this.setRepeatedFixed32Element_(
            fieldNumber, index, value, (writer, fieldNumber, values) => {
                writer.writeRepeatedFixed32(fieldNumber, values);
            });
    }

    /**
     * Sets all fixed32 values into the field for the given field number.
     * All these values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {!Iterable<number>} values
     */
    setUnpackedFixed32Iterable(fieldNumber, values) {
        const array = Array.from(values);
        checkCriticalTypeUnsignedInt32Array(array);
        this.setField_(fieldNumber, array, (writer, fieldNumber, values) => {
            writer.writeRepeatedFixed32(fieldNumber, values);
        });
    }

    /* Fixed64 */

    /**
     * Adds a single fixed64 value into the field for the given field number.
     * All values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {!Int64} value
     */
    addPackedFixed64Element(fieldNumber, value) {
        this.addPackedSfixed64Element(fieldNumber, value);
    }

    /**
     * Adds all fixed64 values into the field for the given field number.
     * All these values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {!Iterable<!Int64>} values
     */
    addPackedFixed64Iterable(fieldNumber, values) {
        this.addPackedSfixed64Iterable(fieldNumber, values);
    }

    /**
     * Adds a single fixed64 value into the field for the given field number.
     * All values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {!Int64} value
     */
    addUnpackedFixed64Element(fieldNumber, value) {
        this.addUnpackedSfixed64Element(fieldNumber, value);
    }

    /**
     * Adds all fixed64 values into the field for the given field number.
     * All these values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {!Iterable<!Int64>} values
     */
    addUnpackedFixed64Iterable(fieldNumber, values) {
        this.addUnpackedSfixed64Iterable(fieldNumber, values);
    }

    /**
     * Sets a single fixed64 value into the field for the given field number at
     * the given index. All values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {number} index
     * @param {!Int64} value
     * @throws {!Error} if index is out of range when check mode is critical
     */
    setPackedFixed64Element(fieldNumber, index, value) {
        this.setPackedSfixed64Element(fieldNumber, index, value);
    }

    /**
     * Sets all fixed64 values into the field for the given field number.
     * All these values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {!Iterable<!Int64>} values
     */
    setPackedFixed64Iterable(fieldNumber, values) {
        this.setPackedSfixed64Iterable(fieldNumber, values);
    }

    /**
     * Sets a single fixed64 value into the field for the given field number at
     * the given index. All values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {number} index
     * @param {!Int64} value
     * @throws {!Error} if index is out of range when check mode is critical
     */
    setUnpackedFixed64Element(fieldNumber, index, value) {
        this.setUnpackedSfixed64Element(fieldNumber, index, value);
    }

    /**
     * Sets all fixed64 values into the field for the given field number.
     * All these values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {!Iterable<!Int64>} values
     */
    setUnpackedFixed64Iterable(fieldNumber, values) {
        this.setUnpackedSfixed64Iterable(fieldNumber, values);
    }

    /* Float */

    /**
     * Adds all float values into the field for the given field number.
     * How these values are encoded depends on the given write function.
     * @param {number} fieldNumber
     * @param {!Iterable<number>} values
     * @param {function(!Writer, number, !Array<number>): undefined} encoder
     * @private
     */
    addRepeatedFloatIterable_(fieldNumber, values, encoder) {
        checkCriticalTypeFloatIterable(values);
        // Eagerly round to 32-bit precision so that reading back after set will
        // yield the same value a reader will receive after serialization.
        const floatValues = Array.from(values, fround);
        const array = [...this.getRepeatedFloatArray_(fieldNumber), ...floatValues];
        checkCriticalTypeFloatIterable(array);
        // Needs to set it back because the default empty array was not cached.
        this.setField_(fieldNumber, array, encoder);
    }

    /**
     * Adds a single float value into the field for the given field number.
     * All values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {number} value
     */
    addPackedFloatElement(fieldNumber, value) {
        this.addRepeatedFloatIterable_(
            fieldNumber, [value], (writer, fieldNumber, values) => {
                writer.writePackedFloat(fieldNumber, values);
            });
    }

    /**
     * Adds all float values into the field for the given field number.
     * All these values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {!Iterable<number>} values
     */
    addPackedFloatIterable(fieldNumber, values) {
        this.addRepeatedFloatIterable_(
            fieldNumber, values, (writer, fieldNumber, values) => {
                writer.writePackedFloat(fieldNumber, values);
            });
    }

    /**
     * Adds a single float value into the field for the given field number.
     * All values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {number} value
     */
    addUnpackedFloatElement(fieldNumber, value) {
        this.addRepeatedFloatIterable_(
            fieldNumber, [value], (writer, fieldNumber, values) => {
                writer.writeRepeatedFloat(fieldNumber, values);
            });
    }

    /**
     * Adds all float values into the field for the given field number.
     * All these values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {!Iterable<number>} values
     */
    addUnpackedFloatIterable(fieldNumber, values) {
        this.addRepeatedFloatIterable_(
            fieldNumber, values, (writer, fieldNumber, values) => {
                writer.writeRepeatedFloat(fieldNumber, values);
            });
    }

    /**
     * Sets a single float value into the field for the given field number at the
     * given index.
     * How these values are encoded depends on the given write function.
     * @param {number} fieldNumber
     * @param {number} index
     * @param {number} value
     * @param {function(!Writer, number, !Array<number>): undefined} encoder
     * @throws {!Error} if index is out of range when check mode is critical
     * @private
     */
    setRepeatedFloatElement_(fieldNumber, index, value, encoder) {
        checkCriticalTypeFloat(value);
        // Eagerly round to 32-bit precision so that reading back after set will
        // yield the same value a reader will receive after serialization.
        const floatValue = Math.fround(value);
        const array = this.getRepeatedFloatArray_(fieldNumber);
        checkCriticalElementIndex(index, array.length);
        array[index] = floatValue;
        // Needs to set it back to set encoder.
        this.setField_(fieldNumber, array, encoder);
    }

    /**
     * Sets a single float value into the field for the given field number at the
     * given index.
     * All values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {number} index
     * @param {number} value
     * @throws {!Error} if index is out of range when check mode is critical
     */
    setPackedFloatElement(fieldNumber, index, value) {
        this.setRepeatedFloatElement_(
            fieldNumber, index, value, (writer, fieldNumber, values) => {
                writer.writePackedFloat(fieldNumber, values);
            });
    }

    /**
     * Sets all float values into the field for the given field number.
     * All these values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {!Iterable<number>} values
     */
    setPackedFloatIterable(fieldNumber, values) {
        checkCriticalTypeFloatIterable(values);
        // Eagerly round to 32-bit precision so that reading back after set will
        // yield the same value a reader will receive after serialization.
        const array = Array.from(values, fround);
        this.setField_(fieldNumber, array, (writer, fieldNumber, values) => {
            writer.writePackedFloat(fieldNumber, values);
        });
    }

    /**
     * Sets a single float value into the field for the given field number at the
     * given index.
     * All values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {number} index
     * @param {number} value
     * @throws {!Error} if index is out of range when check mode is critical
     */
    setUnpackedFloatElement(fieldNumber, index, value) {
        this.setRepeatedFloatElement_(
            fieldNumber, index, value, (writer, fieldNumber, values) => {
                writer.writeRepeatedFloat(fieldNumber, values);
            });
    }

    /**
     * Sets all float values into the field for the given field number.
     * All these values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {!Iterable<number>} values
     */
    setUnpackedFloatIterable(fieldNumber, values) {
        checkCriticalTypeFloatIterable(values);
        // Eagerly round to 32-bit precision so that reading back after set will
        // yield the same value a reader will receive after serialization.
        const array = Array.from(values, fround);
        this.setField_(fieldNumber, array, (writer, fieldNumber, values) => {
            writer.writeRepeatedFloat(fieldNumber, values);
        });
    }

    /* Int32 */

    /**
     * Adds all int32 values into the field for the given field number.
     * How these values are encoded depends on the given write function.
     * @param {number} fieldNumber
     * @param {!Iterable<number>} values
     * @param {function(!Writer, number, !Array<number>): undefined} encoder
     * @private
     */
    addRepeatedInt32Iterable_(fieldNumber, values, encoder) {
        const array = [...this.getRepeatedInt32Array_(fieldNumber), ...values];
        checkCriticalTypeSignedInt32Array(array);
        // Needs to set it back because the default empty array was not cached.
        this.setField_(fieldNumber, array, encoder);
    }

    /**
     * Adds a single int32 value into the field for the given field number.
     * All values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {number} value
     */
    addPackedInt32Element(fieldNumber, value) {
        this.addRepeatedInt32Iterable_(
            fieldNumber, [value], (writer, fieldNumber, values) => {
                writer.writePackedInt32(fieldNumber, values);
            });
    }

    /**
     * Adds all int32 values into the field for the given field number.
     * All these values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {!Iterable<number>} values
     */
    addPackedInt32Iterable(fieldNumber, values) {
        this.addRepeatedInt32Iterable_(
            fieldNumber, values, (writer, fieldNumber, values) => {
                writer.writePackedInt32(fieldNumber, values);
            });
    }

    /**
     * Adds a single int32 value into the field for the given field number.
     * All values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {number} value
     */
    addUnpackedInt32Element(fieldNumber, value) {
        this.addRepeatedInt32Iterable_(
            fieldNumber, [value], (writer, fieldNumber, values) => {
                writer.writeRepeatedInt32(fieldNumber, values);
            });
    }

    /**
     * Adds all int32 values into the field for the given field number.
     * All these values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {!Iterable<number>} values
     */
    addUnpackedInt32Iterable(fieldNumber, values) {
        this.addRepeatedInt32Iterable_(
            fieldNumber, values, (writer, fieldNumber, values) => {
                writer.writeRepeatedInt32(fieldNumber, values);
            });
    }

    /**
     * Sets a single int32 value into the field for the given field number at
     * the given index. How these values are encoded depends on the given write
     * function.
     * @param {number} fieldNumber
     * @param {number} index
     * @param {number} value
     * @param {function(!Writer, number, !Array<number>): undefined} encoder
     * @throws {!Error} if index is out of range when check mode is critical
     * @private
     */
    setRepeatedInt32Element_(fieldNumber, index, value, encoder) {
        checkCriticalTypeSignedInt32(value);
        const array = this.getRepeatedInt32Array_(fieldNumber);
        checkCriticalElementIndex(index, array.length);
        array[index] = value;
        // Needs to set it back to set encoder.
        this.setField_(fieldNumber, array, encoder);
    }

    /**
     * Sets a single int32 value into the field for the given field number at
     * the given index. All values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {number} index
     * @param {number} value
     * @throws {!Error} if index is out of range when check mode is critical
     */
    setPackedInt32Element(fieldNumber, index, value) {
        this.setRepeatedInt32Element_(
            fieldNumber, index, value, (writer, fieldNumber, values) => {
                writer.writePackedInt32(fieldNumber, values);
            });
    }

    /**
     * Sets all int32 values into the field for the given field number.
     * All these values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {!Iterable<number>} values
     */
    setPackedInt32Iterable(fieldNumber, values) {
        const array = Array.from(values);
        checkCriticalTypeSignedInt32Array(array);
        this.setField_(fieldNumber, array, (writer, fieldNumber, values) => {
            writer.writePackedInt32(fieldNumber, values);
        });
    }

    /**
     * Sets a single int32 value into the field for the given field number at
     * the given index. All values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {number} index
     * @param {number} value
     * @throws {!Error} if index is out of range when check mode is critical
     */
    setUnpackedInt32Element(fieldNumber, index, value) {
        this.setRepeatedInt32Element_(
            fieldNumber, index, value, (writer, fieldNumber, values) => {
                writer.writeRepeatedInt32(fieldNumber, values);
            });
    }

    /**
     * Sets all int32 values into the field for the given field number.
     * All these values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {!Iterable<number>} values
     */
    setUnpackedInt32Iterable(fieldNumber, values) {
        const array = Array.from(values);
        checkCriticalTypeSignedInt32Array(array);
        this.setField_(fieldNumber, array, (writer, fieldNumber, values) => {
            writer.writeRepeatedInt32(fieldNumber, values);
        });
    }

    /* Int64 */

    /**
     * Adds all int64 values into the field for the given field number.
     * How these values are encoded depends on the given write function.
     * @param {number} fieldNumber
     * @param {!Iterable<!Int64>} values
     * @param {function(!Writer, number, !Array<!Int64>): undefined} encoder
     * @private
     */
    addRepeatedInt64Iterable_(fieldNumber, values, encoder) {
        const array = [...this.getRepeatedInt64Array_(fieldNumber), ...values];
        checkCriticalTypeSignedInt64Array(array);
        // Needs to set it back because the default empty array was not cached.
        this.setField_(fieldNumber, array, encoder);
    }

    /**
     * Adds a single int64 value into the field for the given field number.
     * All values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {!Int64} value
     */
    addPackedInt64Element(fieldNumber, value) {
        this.addRepeatedInt64Iterable_(
            fieldNumber, [value], (writer, fieldNumber, values) => {
                writer.writePackedInt64(fieldNumber, values);
            });
    }

    /**
     * Adds all int64 values into the field for the given field number.
     * All these values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {!Iterable<!Int64>} values
     */
    addPackedInt64Iterable(fieldNumber, values) {
        this.addRepeatedInt64Iterable_(
            fieldNumber, values, (writer, fieldNumber, values) => {
                writer.writePackedInt64(fieldNumber, values);
            });
    }

    /**
     * Adds a single int64 value into the field for the given field number.
     * All values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {!Int64} value
     */
    addUnpackedInt64Element(fieldNumber, value) {
        this.addRepeatedInt64Iterable_(
            fieldNumber, [value], (writer, fieldNumber, values) => {
                writer.writeRepeatedInt64(fieldNumber, values);
            });
    }

    /**
     * Adds all int64 values into the field for the given field number.
     * All these values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {!Iterable<!Int64>} values
     */
    addUnpackedInt64Iterable(fieldNumber, values) {
        this.addRepeatedInt64Iterable_(
            fieldNumber, values, (writer, fieldNumber, values) => {
                writer.writeRepeatedInt64(fieldNumber, values);
            });
    }

    /**
     * Sets a single int64 value into the field for the given field number at
     * the given index. How these values are encoded depends on the given write
     * function.
     * @param {number} fieldNumber
     * @param {number} index
     * @param {!Int64} value
     * @param {function(!Writer, number, !Array<!Int64>): undefined} encoder
     * @throws {!Error} if index is out of range when check mode is critical
     * @private
     */
    setRepeatedInt64Element_(fieldNumber, index, value, encoder) {
        checkCriticalTypeSignedInt64(value);
        const array = this.getRepeatedInt64Array_(fieldNumber);
        checkCriticalElementIndex(index, array.length);
        array[index] = value;
        // Needs to set it back to set encoder.
        this.setField_(fieldNumber, array, encoder);
    }

    /**
     * Sets a single int64 value into the field for the given field number at
     * the given index. All values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {number} index
     * @param {!Int64} value
     * @throws {!Error} if index is out of range when check mode is critical
     */
    setPackedInt64Element(fieldNumber, index, value) {
        this.setRepeatedInt64Element_(
            fieldNumber, index, value, (writer, fieldNumber, values) => {
                writer.writePackedInt64(fieldNumber, values);
            });
    }

    /**
     * Sets all int64 values into the field for the given field number.
     * All these values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {!Iterable<!Int64>} values
     */
    setPackedInt64Iterable(fieldNumber, values) {
        const array = Array.from(values);
        checkCriticalTypeSignedInt64Array(array);
        this.setField_(fieldNumber, array, (writer, fieldNumber, values) => {
            writer.writePackedInt64(fieldNumber, values);
        });
    }

    /**
     * Sets a single int64 value into the field for the given field number at
     * the given index. All values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {number} index
     * @param {!Int64} value
     * @throws {!Error} if index is out of range when check mode is critical
     */
    setUnpackedInt64Element(fieldNumber, index, value) {
        this.setRepeatedInt64Element_(
            fieldNumber, index, value, (writer, fieldNumber, values) => {
                writer.writeRepeatedInt64(fieldNumber, values);
            });
    }

    /**
     * Sets all int64 values into the field for the given field number.
     * All these values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {!Iterable<!Int64>} values
     */
    setUnpackedInt64Iterable(fieldNumber, values) {
        const array = Array.from(values);
        checkCriticalTypeSignedInt64Array(array);
        this.setField_(fieldNumber, array, (writer, fieldNumber, values) => {
            writer.writeRepeatedInt64(fieldNumber, values);
        });
    }

    /* Sfixed32 */

    /**
     * Adds all sfixed32 values into the field for the given field number.
     * How these values are encoded depends on the given write function.
     * @param {number} fieldNumber
     * @param {!Iterable<number>} values
     * @param {function(!Writer, number, !Array<number>): undefined} encoder
     * @private
     */
    addRepeatedSfixed32Iterable_(fieldNumber, values, encoder) {
        const array = [...this.getRepeatedSfixed32Array_(fieldNumber), ...values];
        checkCriticalTypeSignedInt32Array(array);
        // Needs to set it back because the default empty array was not cached.
        this.setField_(fieldNumber, array, encoder);
    }

    /**
     * Adds a single sfixed32 value into the field for the given field number.
     * All values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {number} value
     */
    addPackedSfixed32Element(fieldNumber, value) {
        this.addRepeatedSfixed32Iterable_(
            fieldNumber, [value], (writer, fieldNumber, values) => {
                writer.writePackedSfixed32(fieldNumber, values);
            });
    }

    /**
     * Adds all sfixed32 values into the field for the given field number.
     * All these values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {!Iterable<number>} values
     */
    addPackedSfixed32Iterable(fieldNumber, values) {
        this.addRepeatedSfixed32Iterable_(
            fieldNumber, values, (writer, fieldNumber, values) => {
                writer.writePackedSfixed32(fieldNumber, values);
            });
    }

    /**
     * Adds a single sfixed32 value into the field for the given field number.
     * All values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {number} value
     */
    addUnpackedSfixed32Element(fieldNumber, value) {
        this.addRepeatedSfixed32Iterable_(
            fieldNumber, [value], (writer, fieldNumber, values) => {
                writer.writeRepeatedSfixed32(fieldNumber, values);
            });
    }

    /**
     * Adds all sfixed32 values into the field for the given field number.
     * All these values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {!Iterable<number>} values
     */
    addUnpackedSfixed32Iterable(fieldNumber, values) {
        this.addRepeatedSfixed32Iterable_(
            fieldNumber, values, (writer, fieldNumber, values) => {
                writer.writeRepeatedSfixed32(fieldNumber, values);
            });
    }

    /**
     * Sets a single sfixed32 value into the field for the given field number at
     * the given index. How these values are encoded depends on the given write
     * function.
     * @param {number} fieldNumber
     * @param {number} index
     * @param {number} value
     * @param {function(!Writer, number, !Array<number>): undefined} encoder
     * @throws {!Error} if index is out of range when check mode is critical
     * @private
     */
    setRepeatedSfixed32Element_(fieldNumber, index, value, encoder) {
        checkCriticalTypeSignedInt32(value);
        const array = this.getRepeatedSfixed32Array_(fieldNumber);
        checkCriticalElementIndex(index, array.length);
        array[index] = value;
        // Needs to set it back to set encoder.
        this.setField_(fieldNumber, array, encoder);
    }

    /**
     * Sets a single sfixed32 value into the field for the given field number at
     * the given index. All values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {number} index
     * @param {number} value
     * @throws {!Error} if index is out of range when check mode is critical
     */
    setPackedSfixed32Element(fieldNumber, index, value) {
        this.setRepeatedSfixed32Element_(
            fieldNumber, index, value, (writer, fieldNumber, values) => {
                writer.writePackedSfixed32(fieldNumber, values);
            });
    }

    /**
     * Sets all sfixed32 values into the field for the given field number.
     * All these values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {!Iterable<number>} values
     */
    setPackedSfixed32Iterable(fieldNumber, values) {
        const array = Array.from(values);
        checkCriticalTypeSignedInt32Array(array);
        this.setField_(fieldNumber, array, (writer, fieldNumber, values) => {
            writer.writePackedSfixed32(fieldNumber, values);
        });
    }

    /**
     * Sets a single sfixed32 value into the field for the given field number at
     * the given index. All values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {number} index
     * @param {number} value
     * @throws {!Error} if index is out of range when check mode is critical
     */
    setUnpackedSfixed32Element(fieldNumber, index, value) {
        this.setRepeatedSfixed32Element_(
            fieldNumber, index, value, (writer, fieldNumber, values) => {
                writer.writeRepeatedSfixed32(fieldNumber, values);
            });
    }

    /**
     * Sets all sfixed32 values into the field for the given field number.
     * All these values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {!Iterable<number>} values
     */
    setUnpackedSfixed32Iterable(fieldNumber, values) {
        const array = Array.from(values);
        checkCriticalTypeSignedInt32Array(array);
        this.setField_(fieldNumber, array, (writer, fieldNumber, values) => {
            writer.writeRepeatedSfixed32(fieldNumber, values);
        });
    }

    /* Sfixed64 */

    /**
     * Adds all sfixed64 values into the field for the given field number.
     * How these values are encoded depends on the given write function.
     * @param {number} fieldNumber
     * @param {!Iterable<!Int64>} values
     * @param {function(!Writer, number, !Array<!Int64>): undefined} encoder
     * @private
     */
    addRepeatedSfixed64Iterable_(fieldNumber, values, encoder) {
        const array = [...this.getRepeatedSfixed64Array_(fieldNumber), ...values];
        checkCriticalTypeSignedInt64Array(array);
        // Needs to set it back because the default empty array was not cached.
        this.setField_(fieldNumber, array, encoder);
    }

    /**
     * Adds a single sfixed64 value into the field for the given field number.
     * All values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {!Int64} value
     */
    addPackedSfixed64Element(fieldNumber, value) {
        this.addRepeatedSfixed64Iterable_(
            fieldNumber, [value], (writer, fieldNumber, values) => {
                writer.writePackedSfixed64(fieldNumber, values);
            });
    }

    /**
     * Adds all sfixed64 values into the field for the given field number.
     * All these values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {!Iterable<!Int64>} values
     */
    addPackedSfixed64Iterable(fieldNumber, values) {
        this.addRepeatedSfixed64Iterable_(
            fieldNumber, values, (writer, fieldNumber, values) => {
                writer.writePackedSfixed64(fieldNumber, values);
            });
    }

    /**
     * Adds a single sfixed64 value into the field for the given field number.
     * All values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {!Int64} value
     */
    addUnpackedSfixed64Element(fieldNumber, value) {
        this.addRepeatedSfixed64Iterable_(
            fieldNumber, [value], (writer, fieldNumber, values) => {
                writer.writeRepeatedSfixed64(fieldNumber, values);
            });
    }

    /**
     * Adds all sfixed64 values into the field for the given field number.
     * All these values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {!Iterable<!Int64>} values
     */
    addUnpackedSfixed64Iterable(fieldNumber, values) {
        this.addRepeatedSfixed64Iterable_(
            fieldNumber, values, (writer, fieldNumber, values) => {
                writer.writeRepeatedSfixed64(fieldNumber, values);
            });
    }

    /**
     * Sets a single sfixed64 value into the field for the given field number at
     * the given index. How these values are encoded depends on the given write
     * function.
     * @param {number} fieldNumber
     * @param {number} index
     * @param {!Int64} value
     * @param {function(!Writer, number, !Array<!Int64>): undefined} encoder
     * @throws {!Error} if index is out of range when check mode is critical
     * @private
     */
    setRepeatedSfixed64Element_(fieldNumber, index, value, encoder) {
        checkCriticalTypeSignedInt64(value);
        const array = this.getRepeatedSfixed64Array_(fieldNumber);
        checkCriticalElementIndex(index, array.length);
        array[index] = value;
        // Needs to set it back to set encoder.
        this.setField_(fieldNumber, array, encoder);
    }

    /**
     * Sets a single sfixed64 value into the field for the given field number at
     * the given index. All values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {number} index
     * @param {!Int64} value
     * @throws {!Error} if index is out of range when check mode is critical
     */
    setPackedSfixed64Element(fieldNumber, index, value) {
        this.setRepeatedSfixed64Element_(
            fieldNumber, index, value, (writer, fieldNumber, values) => {
                writer.writePackedSfixed64(fieldNumber, values);
            });
    }

    /**
     * Sets all sfixed64 values into the field for the given field number.
     * All these values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {!Iterable<!Int64>} values
     */
    setPackedSfixed64Iterable(fieldNumber, values) {
        const array = Array.from(values);
        checkCriticalTypeSignedInt64Array(array);
        this.setField_(fieldNumber, array, (writer, fieldNumber, values) => {
            writer.writePackedSfixed64(fieldNumber, values);
        });
    }

    /**
     * Sets a single sfixed64 value into the field for the given field number at
     * the given index. All values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {number} index
     * @param {!Int64} value
     * @throws {!Error} if index is out of range when check mode is critical
     */
    setUnpackedSfixed64Element(fieldNumber, index, value) {
        this.setRepeatedSfixed64Element_(
            fieldNumber, index, value, (writer, fieldNumber, values) => {
                writer.writeRepeatedSfixed64(fieldNumber, values);
            });
    }

    /**
     * Sets all sfixed64 values into the field for the given field number.
     * All these values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {!Iterable<!Int64>} values
     */
    setUnpackedSfixed64Iterable(fieldNumber, values) {
        const array = Array.from(values);
        checkCriticalTypeSignedInt64Array(array);
        this.setField_(fieldNumber, array, (writer, fieldNumber, values) => {
            writer.writeRepeatedSfixed64(fieldNumber, values);
        });
    }

    /* Sint32 */

    /**
     * Adds all sint32 values into the field for the given field number.
     * How these values are encoded depends on the given write function.
     * @param {number} fieldNumber
     * @param {!Iterable<number>} values
     * @param {function(!Writer, number, !Array<number>): undefined} encoder
     * @private
     */
    addRepeatedSint32Iterable_(fieldNumber, values, encoder) {
        const array = [...this.getRepeatedSint32Array_(fieldNumber), ...values];
        checkCriticalTypeSignedInt32Array(array);
        // Needs to set it back because the default empty array was not cached.
        this.setField_(fieldNumber, array, encoder);
    }

    /**
     * Adds a single sint32 value into the field for the given field number.
     * All values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {number} value
     */
    addPackedSint32Element(fieldNumber, value) {
        this.addRepeatedSint32Iterable_(
            fieldNumber, [value], (writer, fieldNumber, values) => {
                writer.writePackedSint32(fieldNumber, values);
            });
    }

    /**
     * Adds all sint32 values into the field for the given field number.
     * All these values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {!Iterable<number>} values
     */
    addPackedSint32Iterable(fieldNumber, values) {
        this.addRepeatedSint32Iterable_(
            fieldNumber, values, (writer, fieldNumber, values) => {
                writer.writePackedSint32(fieldNumber, values);
            });
    }

    /**
     * Adds a single sint32 value into the field for the given field number.
     * All values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {number} value
     */
    addUnpackedSint32Element(fieldNumber, value) {
        this.addRepeatedSint32Iterable_(
            fieldNumber, [value], (writer, fieldNumber, values) => {
                writer.writeRepeatedSint32(fieldNumber, values);
            });
    }

    /**
     * Adds all sint32 values into the field for the given field number.
     * All these values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {!Iterable<number>} values
     */
    addUnpackedSint32Iterable(fieldNumber, values) {
        this.addRepeatedSint32Iterable_(
            fieldNumber, values, (writer, fieldNumber, values) => {
                writer.writeRepeatedSint32(fieldNumber, values);
            });
    }

    /**
     * Sets a single sint32 value into the field for the given field number at
     * the given index. How these values are encoded depends on the given write
     * function.
     * @param {number} fieldNumber
     * @param {number} index
     * @param {number} value
     * @param {function(!Writer, number, !Array<number>): undefined} encoder
     * @throws {!Error} if index is out of range when check mode is critical
     * @private
     */
    setRepeatedSint32Element_(fieldNumber, index, value, encoder) {
        checkCriticalTypeSignedInt32(value);
        const array = this.getRepeatedSint32Array_(fieldNumber);
        checkCriticalElementIndex(index, array.length);
        array[index] = value;
        // Needs to set it back to set encoder.
        this.setField_(fieldNumber, array, encoder);
    }

    /**
     * Sets a single sint32 value into the field for the given field number at
     * the given index. All values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {number} index
     * @param {number} value
     * @throws {!Error} if index is out of range when check mode is critical
     */
    setPackedSint32Element(fieldNumber, index, value) {
        this.setRepeatedSint32Element_(
            fieldNumber, index, value, (writer, fieldNumber, values) => {
                writer.writePackedSint32(fieldNumber, values);
            });
    }

    /**
     * Sets all sint32 values into the field for the given field number.
     * All these values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {!Iterable<number>} values
     */
    setPackedSint32Iterable(fieldNumber, values) {
        const array = Array.from(values);
        checkCriticalTypeSignedInt32Array(array);
        this.setField_(fieldNumber, array, (writer, fieldNumber, values) => {
            writer.writePackedSint32(fieldNumber, values);
        });
    }

    /**
     * Sets a single sint32 value into the field for the given field number at
     * the given index. All values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {number} index
     * @param {number} value
     * @throws {!Error} if index is out of range when check mode is critical
     */
    setUnpackedSint32Element(fieldNumber, index, value) {
        this.setRepeatedSint32Element_(
            fieldNumber, index, value, (writer, fieldNumber, values) => {
                writer.writeRepeatedSint32(fieldNumber, values);
            });
    }

    /**
     * Sets all sint32 values into the field for the given field number.
     * All these values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {!Iterable<number>} values
     */
    setUnpackedSint32Iterable(fieldNumber, values) {
        const array = Array.from(values);
        checkCriticalTypeSignedInt32Array(array);
        this.setField_(fieldNumber, array, (writer, fieldNumber, values) => {
            writer.writeRepeatedSint32(fieldNumber, values);
        });
    }

    /* Sint64 */

    /**
     * Adds all sint64 values into the field for the given field number.
     * How these values are encoded depends on the given write function.
     * @param {number} fieldNumber
     * @param {!Iterable<!Int64>} values
     * @param {function(!Writer, number, !Array<!Int64>): undefined} encoder
     * @private
     */
    addRepeatedSint64Iterable_(fieldNumber, values, encoder) {
        const array = [...this.getRepeatedSint64Array_(fieldNumber), ...values];
        checkCriticalTypeSignedInt64Array(array);
        // Needs to set it back because the default empty array was not cached.
        this.setField_(fieldNumber, array, encoder);
    }

    /**
     * Adds a single sint64 value into the field for the given field number.
     * All values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {!Int64} value
     */
    addPackedSint64Element(fieldNumber, value) {
        this.addRepeatedSint64Iterable_(
            fieldNumber, [value], (writer, fieldNumber, values) => {
                writer.writePackedSint64(fieldNumber, values);
            });
    }

    /**
     * Adds all sint64 values into the field for the given field number.
     * All these values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {!Iterable<!Int64>} values
     */
    addPackedSint64Iterable(fieldNumber, values) {
        this.addRepeatedSint64Iterable_(
            fieldNumber, values, (writer, fieldNumber, values) => {
                writer.writePackedSint64(fieldNumber, values);
            });
    }

    /**
     * Adds a single sint64 value into the field for the given field number.
     * All values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {!Int64} value
     */
    addUnpackedSint64Element(fieldNumber, value) {
        this.addRepeatedSint64Iterable_(
            fieldNumber, [value], (writer, fieldNumber, values) => {
                writer.writeRepeatedSint64(fieldNumber, values);
            });
    }

    /**
     * Adds all sint64 values into the field for the given field number.
     * All these values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {!Iterable<!Int64>} values
     */
    addUnpackedSint64Iterable(fieldNumber, values) {
        this.addRepeatedSint64Iterable_(
            fieldNumber, values, (writer, fieldNumber, values) => {
                writer.writeRepeatedSint64(fieldNumber, values);
            });
    }

    /**
     * Sets a single sint64 value into the field for the given field number at
     * the given index. How these values are encoded depends on the given write
     * function.
     * @param {number} fieldNumber
     * @param {number} index
     * @param {!Int64} value
     * @param {function(!Writer, number, !Array<!Int64>): undefined} encoder
     * @throws {!Error} if index is out of range when check mode is critical
     * @private
     */
    setRepeatedSint64Element_(fieldNumber, index, value, encoder) {
        checkCriticalTypeSignedInt64(value);
        const array = this.getRepeatedSint64Array_(fieldNumber);
        checkCriticalElementIndex(index, array.length);
        array[index] = value;
        // Needs to set it back to set encoder.
        this.setField_(fieldNumber, array, encoder);
    }

    /**
     * Sets a single sint64 value into the field for the given field number at
     * the given index. All values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {number} index
     * @param {!Int64} value
     * @throws {!Error} if index is out of range when check mode is critical
     */
    setPackedSint64Element(fieldNumber, index, value) {
        this.setRepeatedSint64Element_(
            fieldNumber, index, value, (writer, fieldNumber, values) => {
                writer.writePackedSint64(fieldNumber, values);
            });
    }

    /**
     * Sets all sint64 values into the field for the given field number.
     * All these values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {!Iterable<!Int64>} values
     */
    setPackedSint64Iterable(fieldNumber, values) {
        const array = Array.from(values);
        checkCriticalTypeSignedInt64Array(array);
        this.setField_(fieldNumber, array, (writer, fieldNumber, values) => {
            writer.writePackedSint64(fieldNumber, values);
        });
    }

    /**
     * Sets a single sint64 value into the field for the given field number at
     * the given index. All values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {number} index
     * @param {!Int64} value
     * @throws {!Error} if index is out of range when check mode is critical
     */
    setUnpackedSint64Element(fieldNumber, index, value) {
        this.setRepeatedSint64Element_(
            fieldNumber, index, value, (writer, fieldNumber, values) => {
                writer.writeRepeatedSint64(fieldNumber, values);
            });
    }

    /**
     * Sets all sint64 values into the field for the given field number.
     * All these values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {!Iterable<!Int64>} values
     */
    setUnpackedSint64Iterable(fieldNumber, values) {
        const array = Array.from(values);
        checkCriticalTypeSignedInt64Array(array);
        this.setField_(fieldNumber, array, (writer, fieldNumber, values) => {
            writer.writeRepeatedSint64(fieldNumber, values);
        });
    }

    /* Uint32 */

    /**
     * Adds all uint32 values into the field for the given field number.
     * How these values are encoded depends on the given write function.
     * @param {number} fieldNumber
     * @param {!Iterable<number>} values
     * @param {function(!Writer, number, !Array<number>): undefined} encoder
     * @private
     */
    addRepeatedUint32Iterable_(fieldNumber, values, encoder) {
        const array = [...this.getRepeatedUint32Array_(fieldNumber), ...values];
        checkCriticalTypeUnsignedInt32Array(array);
        // Needs to set it back because the default empty array was not cached.
        this.setField_(fieldNumber, array, encoder);
    }

    /**
     * Adds a single uint32 value into the field for the given field number.
     * All values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {number} value
     */
    addPackedUint32Element(fieldNumber, value) {
        this.addRepeatedUint32Iterable_(
            fieldNumber, [value], (writer, fieldNumber, values) => {
                writer.writePackedUint32(fieldNumber, values);
            });
    }

    /**
     * Adds all uint32 values into the field for the given field number.
     * All these values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {!Iterable<number>} values
     */
    addPackedUint32Iterable(fieldNumber, values) {
        this.addRepeatedUint32Iterable_(
            fieldNumber, values, (writer, fieldNumber, values) => {
                writer.writePackedUint32(fieldNumber, values);
            });
    }

    /**
     * Adds a single uint32 value into the field for the given field number.
     * All values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {number} value
     */
    addUnpackedUint32Element(fieldNumber, value) {
        this.addRepeatedUint32Iterable_(
            fieldNumber, [value], (writer, fieldNumber, values) => {
                writer.writeRepeatedUint32(fieldNumber, values);
            });
    }

    /**
     * Adds all uint32 values into the field for the given field number.
     * All these values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {!Iterable<number>} values
     */
    addUnpackedUint32Iterable(fieldNumber, values) {
        this.addRepeatedUint32Iterable_(
            fieldNumber, values, (writer, fieldNumber, values) => {
                writer.writeRepeatedUint32(fieldNumber, values);
            });
    }

    /**
     * Sets a single uint32 value into the field for the given field number at
     * the given index. How these values are encoded depends on the given write
     * function.
     * @param {number} fieldNumber
     * @param {number} index
     * @param {number} value
     * @param {function(!Writer, number, !Array<number>): undefined} encoder
     * @throws {!Error} if index is out of range when check mode is critical
     * @private
     */
    setRepeatedUint32Element_(fieldNumber, index, value, encoder) {
        checkCriticalTypeUnsignedInt32(value);
        const array = this.getRepeatedUint32Array_(fieldNumber);
        checkCriticalElementIndex(index, array.length);
        array[index] = value;
        // Needs to set it back to set encoder.
        this.setField_(fieldNumber, array, encoder);
    }

    /**
     * Sets a single uint32 value into the field for the given field number at
     * the given index. All values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {number} index
     * @param {number} value
     * @throws {!Error} if index is out of range when check mode is critical
     */
    setPackedUint32Element(fieldNumber, index, value) {
        this.setRepeatedUint32Element_(
            fieldNumber, index, value, (writer, fieldNumber, values) => {
                writer.writePackedUint32(fieldNumber, values);
            });
    }

    /**
     * Sets all uint32 values into the field for the given field number.
     * All these values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {!Iterable<number>} values
     */
    setPackedUint32Iterable(fieldNumber, values) {
        const array = Array.from(values);
        checkCriticalTypeUnsignedInt32Array(array);
        this.setField_(fieldNumber, array, (writer, fieldNumber, values) => {
            writer.writePackedUint32(fieldNumber, values);
        });
    }

    /**
     * Sets a single uint32 value into the field for the given field number at
     * the given index. All values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {number} index
     * @param {number} value
     * @throws {!Error} if index is out of range when check mode is critical
     */
    setUnpackedUint32Element(fieldNumber, index, value) {
        this.setRepeatedUint32Element_(
            fieldNumber, index, value, (writer, fieldNumber, values) => {
                writer.writeRepeatedUint32(fieldNumber, values);
            });
    }

    /**
     * Sets all uint32 values into the field for the given field number.
     * All these values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {!Iterable<number>} values
     */
    setUnpackedUint32Iterable(fieldNumber, values) {
        const array = Array.from(values);
        checkCriticalTypeUnsignedInt32Array(array);
        this.setField_(fieldNumber, array, (writer, fieldNumber, values) => {
            writer.writeRepeatedUint32(fieldNumber, values);
        });
    }

    /* Uint64 */

    /**
     * Adds a single uint64 value into the field for the given field number.
     * All values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {!Int64} value
     */
    addPackedUint64Element(fieldNumber, value) {
        this.addPackedInt64Element(fieldNumber, value);
    }

    /**
     * Adds all uint64 values into the field for the given field number.
     * All these values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {!Iterable<!Int64>} values
     */
    addPackedUint64Iterable(fieldNumber, values) {
        this.addPackedInt64Iterable(fieldNumber, values);
    }

    /**
     * Adds a single uint64 value into the field for the given field number.
     * All values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {!Int64} value
     */
    addUnpackedUint64Element(fieldNumber, value) {
        this.addUnpackedInt64Element(fieldNumber, value);
    }

    /**
     * Adds all uint64 values into the field for the given field number.
     * All these values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {!Iterable<!Int64>} values
     */
    addUnpackedUint64Iterable(fieldNumber, values) {
        this.addUnpackedInt64Iterable(fieldNumber, values);
    }

    /**
     * Sets a single uint64 value into the field for the given field number at
     * the given index. All values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {number} index
     * @param {!Int64} value
     * @throws {!Error} if index is out of range when check mode is critical
     */
    setPackedUint64Element(fieldNumber, index, value) {
        this.setPackedInt64Element(fieldNumber, index, value);
    }

    /**
     * Sets all uint64 values into the field for the given field number.
     * All these values will be encoded as packed values.
     * @param {number} fieldNumber
     * @param {!Iterable<!Int64>} values
     */
    setPackedUint64Iterable(fieldNumber, values) {
        this.setPackedInt64Iterable(fieldNumber, values);
    }

    /**
     * Sets a single uint64 value into the field for the given field number at
     * the given index. All values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {number} index
     * @param {!Int64} value
     * @throws {!Error} if index is out of range when check mode is critical
     */
    setUnpackedUint64Element(fieldNumber, index, value) {
        this.setUnpackedInt64Element(fieldNumber, index, value);
    }

    /**
     * Sets all uint64 values into the field for the given field number.
     * All these values will be encoded as unpacked values.
     * @param {number} fieldNumber
     * @param {!Iterable<!Int64>} values
     */
    setUnpackedUint64Iterable(fieldNumber, values) {
        this.setUnpackedInt64Iterable(fieldNumber, values);
    }

    /* Bytes */

    /**
     * Sets all bytes values into the field for the given field number.
     * @param {number} fieldNumber
     * @param {!Iterable<!ByteString>} values
     */
    setRepeatedBytesIterable(fieldNumber, values) {
        const /** !Array<!ByteString> */ array = Array.from(values);
        checkCriticalTypeByteStringArray(array);
        this.setField_(fieldNumber, array, (writer, fieldNumber, values) => {
            writer.writeRepeatedBytes(fieldNumber, values);
        });
    }

    /**
     * Adds all bytes values into the field for the given field number.
     * @param {number} fieldNumber
     * @param {!Iterable<!ByteString>} values
     */
    addRepeatedBytesIterable(fieldNumber, values) {
        const array = [...this.getRepeatedBytesArray_(fieldNumber), ...values];
        checkCriticalTypeByteStringArray(array);
        // Needs to set it back because the default empty array was not cached.
        this.setField_(fieldNumber, array, (writer, fieldNumber, values) => {
            writer.writeRepeatedBytes(fieldNumber, values);
        });
    }

    /**
     * Sets a single bytes value into the field for the given field number at
     * the given index.
     * @param {number} fieldNumber
     * @param {number} index
     * @param {!ByteString} value
     * @throws {!Error} if index is out of range when check mode is critical
     */
    setRepeatedBytesElement(fieldNumber, index, value) {
        checkCriticalTypeByteString(value);
        const array = this.getRepeatedBytesArray_(fieldNumber);
        checkCriticalElementIndex(index, array.length);
        array[index] = value;
        // Needs to set it back to set encoder.
        this.setField_(fieldNumber, array, (writer, fieldNumber, values) => {
            writer.writeRepeatedBytes(fieldNumber, values);
        });
    }

    /**
     * Adds a single bytes value into the field for the given field number.
     * @param {number} fieldNumber
     * @param {!ByteString} value
     */
    addRepeatedBytesElement(fieldNumber, value) {
        this.addRepeatedBytesIterable(fieldNumber, [value]);
    }

    /* String */

    /**
     * Sets all string values into the field for the given field number.
     * @param {number} fieldNumber
     * @param {!Iterable<string>} values
     */
    setRepeatedStringIterable(fieldNumber, values) {
        const /** !Array<string> */ array = Array.from(values);
        checkCriticalTypeStringArray(array);
        this.setField_(fieldNumber, array, (writer, fieldNumber, values) => {
            writer.writeRepeatedString(fieldNumber, values);
        });
    }

    /**
     * Adds all string values into the field for the given field number.
     * @param {number} fieldNumber
     * @param {!Iterable<string>} values
     */
    addRepeatedStringIterable(fieldNumber, values) {
        const array = [...this.getRepeatedStringArray_(fieldNumber), ...values];
        checkCriticalTypeStringArray(array);
        // Needs to set it back because the default empty array was not cached.
        this.setField_(fieldNumber, array, (writer, fieldNumber, values) => {
            writer.writeRepeatedString(fieldNumber, values);
        });
    }

    /**
     * Sets a single string value into the field for the given field number at
     * the given index.
     * @param {number} fieldNumber
     * @param {number} index
     * @param {string} value
     * @throws {!Error} if index is out of range when check mode is critical
     */
    setRepeatedStringElement(fieldNumber, index, value) {
        checkCriticalTypeString(value);
        const array = this.getRepeatedStringArray_(fieldNumber);
        checkCriticalElementIndex(index, array.length);
        array[index] = value;
        // Needs to set it back to set encoder.
        this.setField_(fieldNumber, array, (writer, fieldNumber, values) => {
            writer.writeRepeatedString(fieldNumber, values);
        });
    }

    /**
     * Adds a single string value into the field for the given field number.
     * @param {number} fieldNumber
     * @param {string} value
     */
    addRepeatedStringElement(fieldNumber, value) {
        this.addRepeatedStringIterable(fieldNumber, [value]);
    }

    /* Message */

    /**
     * Sets all message values into the field for the given field number.
     * @param {number} fieldNumber
     * @param {!Iterable<!InternalMessage>} values
     */
    setRepeatedMessageIterable(fieldNumber, values) {
        const /** !Array<!InternalMessage> */ array = Array.from(values);
        checkCriticalTypeMessageArray(array);
        this.setField_(fieldNumber, array, (writer, fieldNumber, values) => {
            writeRepeatedMessage(writer, fieldNumber, values);
        });
    }

    /**
     * Adds all message values into the field for the given field number.
     * @param {number} fieldNumber
     * @param {!Iterable<!InternalMessage>} values
     * @param {function(!Kernel):!InternalMessage} instanceCreator
     * @param {number=} pivot
     */
    addRepeatedMessageIterable(
        fieldNumber, values, instanceCreator, pivot = undefined) {
        const array = [
            ...this.getRepeatedMessageArray_(fieldNumber, instanceCreator, pivot),
            ...values,
        ];
        checkCriticalTypeMessageArray(array);
        // Needs to set it back with the new array.
        this.setField_(
            fieldNumber, array,
            (writer, fieldNumber, values) =>
                writeRepeatedMessage(writer, fieldNumber, values));
    }

    /**
     * Sets a single message value into the field for the given field number at
     * the given index.
     * @param {number} fieldNumber
     * @param {!InternalMessage} value
     * @param {function(!Kernel):!InternalMessage} instanceCreator
     * @param {number} index
     * @param {number=} pivot
     * @throws {!Error} if index is out of range when check mode is critical
     */
    setRepeatedMessageElement(
        fieldNumber, value, instanceCreator, index, pivot = undefined) {
        checkInstanceCreator(instanceCreator);
        checkCriticalType(
            value !== null, 'Given value is not a message instance: null');
        const array =
            this.getRepeatedMessageArray_(fieldNumber, instanceCreator, pivot);
        checkCriticalElementIndex(index, array.length);
        array[index] = value;
    }

    /**
     * Adds a single message value into the field for the given field number.
     * @param {number} fieldNumber
     * @param {!InternalMessage} value
     * @param {function(!Kernel):!InternalMessage} instanceCreator
     * @param {number=} pivot
     */
    addRepeatedMessageElement(
        fieldNumber, value, instanceCreator, pivot = undefined) {
        this.addRepeatedMessageIterable(
            fieldNumber, [value], instanceCreator, pivot);
    }

    // Groups
    /**
     * Sets all message values into the field for the given field number.
     * @param {number} fieldNumber
     * @param {!Iterable<!InternalMessage>} values
     */
    setRepeatedGroupIterable(fieldNumber, values) {
        const /** !Array<!InternalMessage> */ array = Array.from(values);
        checkCriticalTypeMessageArray(array);
        this.setField_(fieldNumber, array, writeRepeatedGroup);
    }

    /**
     * Adds all message values into the field for the given field number.
     * @param {number} fieldNumber
     * @param {!Iterable<!InternalMessage>} values
     * @param {function(!Kernel):!InternalMessage} instanceCreator
     * @param {number=} pivot
     */
    addRepeatedGroupIterable(
        fieldNumber, values, instanceCreator, pivot = undefined) {
        const array = [
            ...this.getRepeatedGroupArray_(fieldNumber, instanceCreator, pivot),
            ...values,
        ];
        checkCriticalTypeMessageArray(array);
        // Needs to set it back with the new array.
        this.setField_(fieldNumber, array, writeRepeatedGroup);
    }

    /**
     * Sets a single message value into the field for the given field number at
     * the given index.
     * @param {number} fieldNumber
     * @param {!InternalMessage} value
     * @param {function(!Kernel):!InternalMessage} instanceCreator
     * @param {number} index
     * @param {number=} pivot
     * @throws {!Error} if index is out of range when check mode is critical
     */
    setRepeatedGroupElement(
        fieldNumber, value, instanceCreator, index, pivot = undefined) {
        checkInstanceCreator(instanceCreator);
        checkCriticalType(
            value !== null, 'Given value is not a message instance: null');
        const array =
            this.getRepeatedGroupArray_(fieldNumber, instanceCreator, pivot);
        checkCriticalElementIndex(index, array.length);
        array[index] = value;
    }

    /**
     * Adds a single message value into the field for the given field number.
     * @param {number} fieldNumber
     * @param {!InternalMessage} value
     * @param {function(!Kernel):!InternalMessage} instanceCreator
     * @param {number=} pivot
     */
    addRepeatedGroupElement(
        fieldNumber, value, instanceCreator, pivot = undefined) {
        this.addRepeatedGroupIterable(fieldNumber, [value], instanceCreator, pivot);
    }
}

exports = Kernel;
